✨ 初始化项目
This commit is contained in:
parent
b4dc3c7305
commit
533ede4f66
27
.gitignore
vendored
27
.gitignore
vendored
@ -1,23 +1,4 @@
|
|||||||
# ---> Go
|
.idea/
|
||||||
# If you prefer the allow list template instead of the deny list, see community template:
|
*/.DS_Store
|
||||||
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
.vscode
|
||||||
#
|
go.sum
|
||||||
# Binaries for programs and plugins
|
|
||||||
*.exe
|
|
||||||
*.exe~
|
|
||||||
*.dll
|
|
||||||
*.so
|
|
||||||
*.dylib
|
|
||||||
|
|
||||||
# Test binary, built with `go test -c`
|
|
||||||
*.test
|
|
||||||
|
|
||||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
|
||||||
*.out
|
|
||||||
|
|
||||||
# Dependency directories (remove the comment below to include it)
|
|
||||||
# vendor/
|
|
||||||
|
|
||||||
# Go workspace file
|
|
||||||
go.work
|
|
||||||
|
|
305
account/api.go
Normal file
305
account/api.go
Normal file
@ -0,0 +1,305 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Author:1711788888@qq.com
|
||||||
|
* @Date: 2021/5/27 20:07
|
||||||
|
* @Desc: 账号管理
|
||||||
|
*/
|
||||||
|
|
||||||
|
package account
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/core"
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/enum"
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
serviceAccount = "im_open_login_svc"
|
||||||
|
serviceOpenIM = "openim"
|
||||||
|
commandImportAccount = "account_import"
|
||||||
|
commandImportAccounts = "multiaccount_import"
|
||||||
|
commandDeleteAccounts = "account_delete"
|
||||||
|
commandCheckAccounts = "account_check"
|
||||||
|
commandKickAccount = "kick"
|
||||||
|
commandQueryAccountsOnlineStatus = "query_online_status"
|
||||||
|
|
||||||
|
batchImportAccountsLimit = 100 // 导入账号限制
|
||||||
|
batchDeleteAccountsLimit = 100 // 删除账号限制
|
||||||
|
batchCheckAccountsLimit = 100 // 查询账号限制
|
||||||
|
)
|
||||||
|
|
||||||
|
type API interface {
|
||||||
|
// ImportAccount 导入单个帐号
|
||||||
|
// 本接口用于将 App 自有帐号导入即时通信 IM 帐号系统,
|
||||||
|
// 为该帐号创建一个对应的内部 ID,使该帐号能够使用即时通信 IM 服务。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/1608
|
||||||
|
ImportAccount(account *Account) (err error)
|
||||||
|
|
||||||
|
// ImportAccounts 导入多个帐号
|
||||||
|
// 本接口用于批量将 App 自有帐号导入即时通信 IM 帐号系统,
|
||||||
|
// 为该帐号创建一个对应的内部 ID,使该帐号能够使用即时通信 IM 服务。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/4919
|
||||||
|
ImportAccounts(userIds ...string) (failUserIds []string, err error)
|
||||||
|
|
||||||
|
// DeleteAccount 删除账号
|
||||||
|
// 本方法拓展于“删除多个帐号(DeleteAccounts)”方法。
|
||||||
|
// 仅支持删除套餐包类型为 IM 体验版的帐号,其他类型的账号(如:TRTC、白板、专业版、旗舰版)无法删除。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/36443
|
||||||
|
DeleteAccount(userId string) (err error)
|
||||||
|
|
||||||
|
// DeleteAccounts 删除多个帐号
|
||||||
|
// 仅支持删除套餐包类型为 IM 体验版的帐号,其他类型的账号(如:TRTC、白板、专业版、旗舰版)无法删除。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/36443
|
||||||
|
DeleteAccounts(userIds ...string) (results []*DeleteResult, err error)
|
||||||
|
|
||||||
|
// CheckAccount 查询帐号导入状态
|
||||||
|
// 本方法拓展于“查询多个帐号导入状态(CheckAccounts)”方法。
|
||||||
|
// 用于查询自有帐号是否已导入即时通信 IM。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/38417
|
||||||
|
CheckAccount(userId string) (bool, error)
|
||||||
|
|
||||||
|
// CheckAccounts 查询多个帐号导入状态
|
||||||
|
// 用于查询自有帐号是否已导入即时通信 IM,支持批量查询。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/38417
|
||||||
|
CheckAccounts(userIds ...string) (results []*CheckResult, err error)
|
||||||
|
|
||||||
|
// KickAccount 使帐号登录状态失效
|
||||||
|
// 本接口适用于将 App 用户帐号的登录状态(例如 UserSig)失效。
|
||||||
|
// 例如,开发者判断一个用户为恶意帐号后,可以调用本接口将该用户当前的登录状态失效,这样用户使用历史 UserSig 登录即时通信 IM 会失败。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/3853
|
||||||
|
KickAccount(userId string) (err error)
|
||||||
|
|
||||||
|
// GetAccountOnlineState 查询帐号在线状态
|
||||||
|
// 本方法拓展于“查询多个帐号在线状态(GetAccountsOnlineState)”方法。
|
||||||
|
// 获取用户当前的登录状态。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/2566
|
||||||
|
GetAccountOnlineState(userId string, isNeedDetail ...bool) (*OnlineStatusResult, error)
|
||||||
|
|
||||||
|
// GetAccountsOnlineState 查询多个帐号在线状态
|
||||||
|
// 获取用户当前的登录状态。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/2566
|
||||||
|
GetAccountsOnlineState(userIds []string, isNeedDetail ...bool) (ret *OnlineStatusRet, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type api struct {
|
||||||
|
client core.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAPI(client core.Client) API {
|
||||||
|
return &api{client: client}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImportAccount 导入单个帐号
|
||||||
|
// 本接口用于将 App 自有帐号导入即时通信 IM 帐号系统,
|
||||||
|
// 为该帐号创建一个对应的内部 ID,使该帐号能够使用即时通信 IM 服务。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/1608
|
||||||
|
func (a *api) ImportAccount(account *Account) (err error) {
|
||||||
|
if err = a.client.Post(serviceAccount, commandImportAccount, account, &types.ActionBaseResp{}); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImportAccounts 导入多个帐号
|
||||||
|
// 本接口用于批量将 App 自有帐号导入即时通信 IM 帐号系统,
|
||||||
|
// 为该帐号创建一个对应的内部 ID,使该帐号能够使用即时通信 IM 服务。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/4919
|
||||||
|
func (a *api) ImportAccounts(userIds ...string) (failUserIds []string, err error) {
|
||||||
|
if c := len(userIds); c == 0 {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, "the userid is not set")
|
||||||
|
return
|
||||||
|
} else if c > batchImportAccountsLimit {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, fmt.Sprintf("the number of imported accounts cannot exceed %d", batchImportAccountsLimit))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &importAccountsReq{UserIds: userIds}
|
||||||
|
resp := &importAccountsResp{}
|
||||||
|
|
||||||
|
if err = a.client.Post(serviceAccount, commandImportAccounts, req, resp); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
failUserIds = resp.FailUserIds
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteAccount 删除账号
|
||||||
|
// 本方法拓展于“删除多个帐号(DeleteAccounts)”方法。
|
||||||
|
// 仅支持删除套餐包类型为 IM 体验版的帐号,其他类型的账号(如:TRTC、白板、专业版、旗舰版)无法删除。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/36443
|
||||||
|
func (a *api) DeleteAccount(userId string) (err error) {
|
||||||
|
results, err := a.DeleteAccounts(userId)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, result := range results {
|
||||||
|
if result.UserId == userId && result.ResultCode != enum.SuccessCode {
|
||||||
|
return core.NewError(result.ResultCode, result.ResultInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteAccounts 删除多个帐号
|
||||||
|
// 仅支持删除套餐包类型为 IM 体验版的帐号,其他类型的账号(如:TRTC、白板、专业版、旗舰版)无法删除。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/36443
|
||||||
|
func (a *api) DeleteAccounts(userIds ...string) (results []*DeleteResult, err error) {
|
||||||
|
if c := len(userIds); c == 0 {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, "the userid is not set")
|
||||||
|
return
|
||||||
|
} else if c > batchDeleteAccountsLimit {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, fmt.Sprintf("the number of deleted accounts cannot exceed %d", batchDeleteAccountsLimit))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &deleteAccountsReq{}
|
||||||
|
resp := &deleteAccountsResp{}
|
||||||
|
|
||||||
|
for _, userId := range userIds {
|
||||||
|
req.Deletes = append(req.Deletes, &accountItem{userId})
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = a.client.Post(serviceAccount, commandDeleteAccounts, req, resp); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
results = resp.Results
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckAccount 查询帐号导入状态.
|
||||||
|
// 本方法拓展于“查询多个帐号导入状态(CheckAccounts)”方法。
|
||||||
|
// 用于查询自有帐号是否已导入即时通信 IM。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/38417
|
||||||
|
func (a *api) CheckAccount(userId string) (bool, error) {
|
||||||
|
results, err := a.CheckAccounts(userId)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, result := range results {
|
||||||
|
if result.UserId == userId {
|
||||||
|
if result.ResultCode != enum.SuccessCode {
|
||||||
|
return false, core.NewError(result.ResultCode, result.ResultInfo)
|
||||||
|
} else {
|
||||||
|
return result.Status == ImportedStatusYes, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckAccounts 查询多个帐号导入状态.
|
||||||
|
// 用于查询自有帐号是否已导入即时通信 IM,支持批量查询。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/38417
|
||||||
|
func (a *api) CheckAccounts(userIds ...string) (results []*CheckResult, err error) {
|
||||||
|
if c := len(userIds); c == 0 {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, "the account is not set")
|
||||||
|
return
|
||||||
|
} else if c > batchCheckAccountsLimit {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, fmt.Sprintf("the number of checked accounts cannot exceed %d", batchCheckAccountsLimit))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &checkAccountsReq{}
|
||||||
|
resp := &checkAccountsResp{}
|
||||||
|
|
||||||
|
for _, userId := range userIds {
|
||||||
|
req.Checks = append(req.Checks, &accountItem{userId})
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = a.client.Post(serviceAccount, commandCheckAccounts, req, resp); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
results = resp.Results
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// KickAccount 失效帐号登录状态
|
||||||
|
// 本接口适用于将 App 用户帐号的登录状态(例如 UserSig)失效。
|
||||||
|
// 例如,开发者判断一个用户为恶意帐号后,可以调用本接口将该用户当前的登录状态失效,这样用户使用历史 UserSig 登录即时通信 IM 会失败。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/3853
|
||||||
|
func (a *api) KickAccount(userId string) (err error) {
|
||||||
|
if err = a.client.Post(serviceAccount, commandKickAccount, &kickAccountReq{userId}, &types.ActionBaseResp{}); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAccountOnlineState 查询帐号在线状态
|
||||||
|
// 本方法拓展于“查询多个帐号在线状态(GetAccountsOnlineState)”方法。
|
||||||
|
// 获取用户当前的登录状态。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/2566
|
||||||
|
func (a *api) GetAccountOnlineState(userId string, isNeedDetail ...bool) (*OnlineStatusResult, error) {
|
||||||
|
ret, err := a.GetAccountsOnlineState([]string{userId}, isNeedDetail...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range ret.Errors {
|
||||||
|
if item.UserId == userId && item.ErrorCode != enum.SuccessCode {
|
||||||
|
return nil, core.NewError(item.ErrorCode, "account exception")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range ret.Results {
|
||||||
|
if item.UserId == userId {
|
||||||
|
return &item, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAccountsOnlineState 查询多个帐号在线状态
|
||||||
|
// 获取用户当前的登录状态。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/2566
|
||||||
|
func (a *api) GetAccountsOnlineState(userIds []string, isNeedDetail ...bool) (ret *OnlineStatusRet, err error) {
|
||||||
|
req := &queryAccountsOnlineStatusReq{UserIds: userIds}
|
||||||
|
resp := &queryAccountsOnlineStatusResp{}
|
||||||
|
|
||||||
|
if len(isNeedDetail) > 0 && isNeedDetail[0] {
|
||||||
|
req.IsNeedDetail = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = a.client.Post(serviceOpenIM, commandQueryAccountsOnlineStatus, req, resp); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = &OnlineStatusRet{
|
||||||
|
Results: resp.Results,
|
||||||
|
Errors: resp.Errors,
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
16
account/enum.go
Normal file
16
account/enum.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Author:1711788888@qq.com
|
||||||
|
* @Date: 2021/9/7 11:09
|
||||||
|
* @Desc: TODO
|
||||||
|
*/
|
||||||
|
|
||||||
|
package account
|
||||||
|
|
||||||
|
// ImportedStatusType 导入状态
|
||||||
|
type ImportedStatusType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
ImportedStatusNo ImportedStatusType = "NotImported" // 未导入
|
||||||
|
ImportedStatusYes ImportedStatusType = "Imported" // 已导入
|
||||||
|
)
|
117
account/types.go
Normal file
117
account/types.go
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Author:1711788888@qq.com
|
||||||
|
* @Date: 2021/5/29 17:36
|
||||||
|
* @Desc: 账号管理数据类型
|
||||||
|
*/
|
||||||
|
|
||||||
|
package account
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// Account 导入单个账号
|
||||||
|
Account struct {
|
||||||
|
UserId string `json:"Identifier"` // (必填)用户名,长度不超过32字节
|
||||||
|
Nickname string `json:"Nick"` // (选填)用户昵称
|
||||||
|
FaceUrl string `json:"FaceUrl"` // (选填)用户头像 URL
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量导入账号(参数)
|
||||||
|
importAccountsReq struct {
|
||||||
|
UserIds []string `json:"Accounts"` // (必填)用户名,单个用户名长度不超过32字节,单次最多导入100个用户名
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量导入账号(响应)
|
||||||
|
importAccountsResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
FailUserIds []string `json:"FailAccounts"` // 导入失败的帐号列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// 账号项
|
||||||
|
accountItem struct {
|
||||||
|
UserId string `json:"UserID"` // 帐号的UserID
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除多个帐号(请求)
|
||||||
|
deleteAccountsReq struct {
|
||||||
|
Deletes []*accountItem `json:"DeleteItem"` // 请求删除的帐号对象数组,单次请求最多支持100个帐号
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除多个账号(响应)
|
||||||
|
deleteAccountsResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
Results []*DeleteResult `json:"ResultItem"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteResult 删除多个账号结果项
|
||||||
|
DeleteResult struct {
|
||||||
|
ResultCode int `json:"ResultCode"` // 单个帐号的错误码,0表示成功,非0表示失败
|
||||||
|
ResultInfo string `json:"ResultInfo"` // 单个帐号删除失败时的错误描述信息
|
||||||
|
UserId string `json:"UserID"` // 请求删除的帐号的 UserID
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询多个帐号(请求)
|
||||||
|
checkAccountsReq struct {
|
||||||
|
Checks []*accountItem `json:"CheckItem"` // (必填)请求检查的帐号对象数组,单次请求最多支持100个帐号
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询多个帐号(响应)
|
||||||
|
checkAccountsResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
Results []*CheckResult `json:"ResultItem"` // 检测结果
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckResult 检测结果
|
||||||
|
CheckResult struct {
|
||||||
|
UserId string `json:"UserID"` // 请求检查的帐号的 UserID
|
||||||
|
Status ImportedStatusType `json:"AccountStatus"` // 单个帐号的导入状态:Imported 表示已导入,NotImported 表示未导入
|
||||||
|
ResultCode int `json:"ResultCode"` // 单个帐号的检查结果:0表示成功,非0表示失败
|
||||||
|
ResultInfo string `json:"ResultInfo"` // 单个帐号检查失败时的错误描述信息
|
||||||
|
}
|
||||||
|
|
||||||
|
// 失效帐号登录状态(请求)
|
||||||
|
kickAccountReq struct {
|
||||||
|
UserId string `json:"Identifier"` // (必填)用户名
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询帐号在线状态(请求)
|
||||||
|
queryAccountsOnlineStatusReq struct {
|
||||||
|
UserIds []string `json:"To_Account"` // (必填)需要查询这些 UserID 的登录状态,一次最多查询500个 UserID 的状态
|
||||||
|
IsNeedDetail int `json:"IsNeedDetail"` // (选填)是否需要返回详细的登录平台信息。0表示不需要,1表示需要
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询帐号在线状态(响应)
|
||||||
|
queryAccountsOnlineStatusResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
Results []OnlineStatusResult `json:"QueryResult"` // 用户在线状态结构化信息
|
||||||
|
Errors []OnlineStatusError `json:"ErrorList"` // 状态查询失败的帐号列表,在此列表中的目标帐号,状态查询失败或目标帐号不存在。若状态全部查询成功,则 ErrorList 为空
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnlineStatusRet 在线状态结果
|
||||||
|
OnlineStatusRet struct {
|
||||||
|
Results []OnlineStatusResult // 用户在线状态结构化信息
|
||||||
|
Errors []OnlineStatusError // 状态查询失败的帐号列表,在此列表中的目标帐号,状态查询失败或目标帐号不存在。若状态全部查询成功,则 ErrorList 为空
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnlineStatusPlatform 详细的登录平台信息
|
||||||
|
OnlineStatusPlatform struct {
|
||||||
|
Platform string `json:"Platform"` // 登录的平台类型。可能的返回值有:"iPhone", "Android", "Web", "PC", "iPad", "Mac"。
|
||||||
|
Status string `json:"Status"` // 该登录平台的状态
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnlineStatusResult 用户在线状态结构化信息项
|
||||||
|
OnlineStatusResult struct {
|
||||||
|
UserId string `json:"To_Account"` // 用户的 UserID
|
||||||
|
Status string `json:"Status"` // 用户状态,前台运行状态(Online)、后台运行状态(PushOnline)、未登录状态(Offline)
|
||||||
|
Detail []OnlineStatusPlatform `json:"Detail"` // 详细的登录平台信息
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnlineStatusError 状态查询失败的帐号项
|
||||||
|
OnlineStatusError struct {
|
||||||
|
UserId string `json:"To_Account"` // 状态查询失败的目标帐号
|
||||||
|
ErrorCode int `json:"ErrorCode"` // 状态查询失败的错误码,若目标帐号的错误码为70107,表示该帐号不存在
|
||||||
|
}
|
||||||
|
)
|
289
callback/callback.go
Normal file
289
callback/callback.go
Normal file
@ -0,0 +1,289 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Author:1711788888@qq.com
|
||||||
|
* @Date: 2021/5/27 14:24
|
||||||
|
* @Desc: TODO
|
||||||
|
*/
|
||||||
|
|
||||||
|
package callback
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
commandStateChange = "State.StateChange"
|
||||||
|
commandBeforeFriendAdd = "Sns.CallbackPrevFriendAdd"
|
||||||
|
commandBeforeFriendResponse = "Sns.CallbackPrevFriendResponse"
|
||||||
|
commandAfterFriendAdd = "Sns.CallbackFriendAdd"
|
||||||
|
commandAfterFriendDelete = "Sns.CallbackFriendDelete"
|
||||||
|
commandAfterBlacklistAdd = "Sns.CallbackBlackListAdd"
|
||||||
|
commandAfterBlacklistDelete = "Sns.CallbackBlackListDelete"
|
||||||
|
commandBeforePrivateMessageSend = "C2C.CallbackBeforeSendMsg"
|
||||||
|
commandAfterPrivateMessageSend = "C2C.CallbackAfterSendMsg"
|
||||||
|
commandAfterPrivateMessageReport = "C2C.CallbackAfterMsgReport"
|
||||||
|
commandAfterPrivateMessageRevoke = "C2C.CallbackAfterMsgWithDraw"
|
||||||
|
commandBeforeGroupCreate = "Group.CallbackBeforeCreateGroup"
|
||||||
|
commandAfterGroupCreate = "Group.CallbackAfterCreateGroup"
|
||||||
|
commandBeforeApplyJoinGroup = "Group.CallbackBeforeApplyJoinGroup"
|
||||||
|
commandBeforeInviteJoinGroup = "Group.CallbackBeforeInviteJoinGroup"
|
||||||
|
commandAfterNewMemberJoinGroup = "Group.CallbackAfterNewMemberJoin"
|
||||||
|
commandAfterMemberExitGroup = "Group.CallbackAfterMemberExit"
|
||||||
|
commandBeforeGroupMessageSend = "Group.CallbackBeforeSendMsg"
|
||||||
|
commandAfterGroupMessageSend = "Group.CallbackAfterSendMsg"
|
||||||
|
commandAfterGroupFull = "Group.CallbackAfterGroupFull"
|
||||||
|
commandAfterGroupDestroyed = "Group.CallbackAfterGroupDestroyed"
|
||||||
|
commandAfterGroupInfoChanged = "Group.CallbackAfterGroupInfoChanged"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
EventStateChange Event = iota + 1
|
||||||
|
EventBeforeFriendAdd
|
||||||
|
EventBeforeFriendResponse
|
||||||
|
EventAfterFriendAdd
|
||||||
|
EventAfterFriendDelete
|
||||||
|
EventAfterBlacklistAdd
|
||||||
|
EventAfterBlacklistDelete
|
||||||
|
EventBeforePrivateMessageSend
|
||||||
|
EventAfterPrivateMessageSend
|
||||||
|
EventAfterPrivateMessageReport
|
||||||
|
EventAfterPrivateMessageRevoke
|
||||||
|
EventBeforeGroupCreate
|
||||||
|
EventAfterGroupCreate
|
||||||
|
EventBeforeApplyJoinGroup
|
||||||
|
EventBeforeInviteJoinGroup
|
||||||
|
EventAfterNewMemberJoinGroup
|
||||||
|
EventAfterMemberExitGroup
|
||||||
|
EventBeforeGroupMessageSend
|
||||||
|
EventAfterGroupMessageSend
|
||||||
|
EventAfterGroupFull
|
||||||
|
EventAfterGroupDestroyed
|
||||||
|
EventAfterGroupInfoChanged
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ackSuccessStatus = "OK"
|
||||||
|
ackFailureStatus = "FAIL"
|
||||||
|
|
||||||
|
ackSuccessCode = 0
|
||||||
|
ackFailureCode = 1
|
||||||
|
|
||||||
|
queryAppId = "SdkAppid"
|
||||||
|
queryCommand = "CallbackCommand"
|
||||||
|
queryClientId = "ClientIP"
|
||||||
|
queryOptPlatform = "OptPlatform"
|
||||||
|
queryContentType = "contenttype"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
Event int
|
||||||
|
EventHandlerFunc func(ack Ack, data interface{})
|
||||||
|
Options struct {
|
||||||
|
SdkAppId int
|
||||||
|
}
|
||||||
|
|
||||||
|
Callback interface {
|
||||||
|
// Register 注册事件
|
||||||
|
Register(event Event, handler EventHandlerFunc)
|
||||||
|
// Listen 监听事件
|
||||||
|
Listen(w http.ResponseWriter, r *http.Request)
|
||||||
|
}
|
||||||
|
|
||||||
|
callback struct {
|
||||||
|
appId int
|
||||||
|
mu sync.Mutex
|
||||||
|
handlers map[Event]EventHandlerFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
Ack interface {
|
||||||
|
// Ack 应答
|
||||||
|
Ack(resp interface{}) error
|
||||||
|
// AckFailure 失败应答
|
||||||
|
AckFailure(message ...string) error
|
||||||
|
// AckSuccess 成功应答
|
||||||
|
AckSuccess(code int, message ...string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
ack struct {
|
||||||
|
w http.ResponseWriter
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewCallback(appId int) Callback {
|
||||||
|
return &callback{
|
||||||
|
appId: appId,
|
||||||
|
handlers: make(map[Event]EventHandlerFunc),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register 注册事件
|
||||||
|
func (c *callback) Register(event Event, handler EventHandlerFunc) {
|
||||||
|
c.mu.Lock()
|
||||||
|
c.handlers[event] = handler
|
||||||
|
c.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen 监听事件
|
||||||
|
func (c *callback) Listen(w http.ResponseWriter, r *http.Request) {
|
||||||
|
a := newAck(w)
|
||||||
|
|
||||||
|
appId, ok := c.GetQuery(r, queryAppId)
|
||||||
|
if !ok || appId != strconv.Itoa(c.appId) {
|
||||||
|
_ = a.AckFailure("invalid sdk appId")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
command, ok := c.GetQuery(r, queryCommand)
|
||||||
|
if !ok {
|
||||||
|
_ = a.AckFailure("invalid callback command")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
|
_ = r.Body.Close()
|
||||||
|
if err != nil {
|
||||||
|
_ = a.AckFailure(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if event, data, err := c.parseCommand(command, body); err != nil {
|
||||||
|
_ = a.AckFailure(err.Error())
|
||||||
|
} else {
|
||||||
|
if fn, ok := c.handlers[event]; ok {
|
||||||
|
fn(a, data)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
_ = a.AckSuccess(ackSuccessCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseCommand parse command and body package.
|
||||||
|
func (c *callback) parseCommand(command string, body []byte) (event Event, data interface{}, err error) {
|
||||||
|
switch command {
|
||||||
|
case commandStateChange:
|
||||||
|
event = EventStateChange
|
||||||
|
data = &StateChange{}
|
||||||
|
case commandBeforeFriendAdd:
|
||||||
|
event = EventBeforeFriendAdd
|
||||||
|
data = &BeforeFriendAdd{}
|
||||||
|
case commandBeforeFriendResponse:
|
||||||
|
event = EventBeforeFriendResponse
|
||||||
|
data = &BeforeFriendResponse{}
|
||||||
|
case commandAfterFriendAdd:
|
||||||
|
event = EventAfterFriendAdd
|
||||||
|
data = &AfterFriendAdd{}
|
||||||
|
case commandAfterFriendDelete:
|
||||||
|
event = EventAfterFriendDelete
|
||||||
|
data = &AfterFriendDelete{}
|
||||||
|
case commandAfterBlacklistAdd:
|
||||||
|
event = EventAfterBlacklistAdd
|
||||||
|
data = &AfterBlacklistAdd{}
|
||||||
|
case commandAfterBlacklistDelete:
|
||||||
|
event = EventAfterBlacklistDelete
|
||||||
|
data = &AfterBlacklistDelete{}
|
||||||
|
case commandBeforePrivateMessageSend:
|
||||||
|
event = EventBeforePrivateMessageSend
|
||||||
|
data = &BeforePrivateMessageSend{}
|
||||||
|
case commandAfterPrivateMessageSend:
|
||||||
|
event = EventAfterPrivateMessageSend
|
||||||
|
data = &AfterPrivateMessageSend{}
|
||||||
|
case commandAfterPrivateMessageReport:
|
||||||
|
event = EventAfterPrivateMessageReport
|
||||||
|
data = &AfterPrivateMessageReport{}
|
||||||
|
case commandAfterPrivateMessageRevoke:
|
||||||
|
event = EventAfterPrivateMessageRevoke
|
||||||
|
data = &AfterPrivateMessageRevoke{}
|
||||||
|
case commandBeforeGroupCreate:
|
||||||
|
event = EventBeforeGroupCreate
|
||||||
|
data = &BeforeGroupCreate{}
|
||||||
|
case commandAfterGroupCreate:
|
||||||
|
event = EventAfterGroupCreate
|
||||||
|
data = &AfterGroupCreate{}
|
||||||
|
case commandBeforeApplyJoinGroup:
|
||||||
|
event = EventBeforeApplyJoinGroup
|
||||||
|
data = &BeforeApplyJoinGroup{}
|
||||||
|
case commandBeforeInviteJoinGroup:
|
||||||
|
event = EventBeforeInviteJoinGroup
|
||||||
|
data = &BeforeInviteJoinGroup{}
|
||||||
|
case commandAfterNewMemberJoinGroup:
|
||||||
|
event = EventAfterNewMemberJoinGroup
|
||||||
|
data = &AfterNewMemberJoinGroup{}
|
||||||
|
case commandAfterMemberExitGroup:
|
||||||
|
event = EventAfterMemberExitGroup
|
||||||
|
data = &AfterMemberExitGroup{}
|
||||||
|
case commandBeforeGroupMessageSend:
|
||||||
|
event = EventBeforeGroupMessageSend
|
||||||
|
data = &BeforeGroupMessageSend{}
|
||||||
|
case commandAfterGroupMessageSend:
|
||||||
|
event = EventAfterGroupMessageSend
|
||||||
|
data = &AfterGroupMessageSend{}
|
||||||
|
case commandAfterGroupFull:
|
||||||
|
event = EventAfterGroupFull
|
||||||
|
data = &AfterGroupFull{}
|
||||||
|
case commandAfterGroupDestroyed:
|
||||||
|
event = EventAfterGroupDestroyed
|
||||||
|
data = &AfterGroupDestroyed{}
|
||||||
|
case commandAfterGroupInfoChanged:
|
||||||
|
event = EventAfterGroupInfoChanged
|
||||||
|
data = &AfterGroupInfoChanged{}
|
||||||
|
default:
|
||||||
|
return 0, nil, errors.New("invalid callback command")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = json.Unmarshal(body, &data); err != nil {
|
||||||
|
return 0, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return event, data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetQuery 获取查询参数
|
||||||
|
func (c *callback) GetQuery(r *http.Request, key string) (string, bool) {
|
||||||
|
if values, ok := r.URL.Query()[key]; ok {
|
||||||
|
return values[0], ok
|
||||||
|
} else {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAck(w http.ResponseWriter) Ack {
|
||||||
|
return &ack{w}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ack 应答
|
||||||
|
func (a *ack) Ack(resp interface{}) error {
|
||||||
|
b, _ := json.Marshal(resp)
|
||||||
|
a.w.WriteHeader(http.StatusOK)
|
||||||
|
_, err := a.w.Write(b)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AckFailure 应答失败
|
||||||
|
func (a *ack) AckFailure(message ...string) error {
|
||||||
|
resp := BaseResp{}
|
||||||
|
resp.ActionStatus = ackFailureStatus
|
||||||
|
resp.ErrorCode = ackFailureCode
|
||||||
|
if len(message) > 0 {
|
||||||
|
resp.ErrorInfo = message[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
return a.Ack(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AckSuccess 应答成功
|
||||||
|
func (a *ack) AckSuccess(code int, message ...string) error {
|
||||||
|
resp := BaseResp{}
|
||||||
|
resp.ActionStatus = ackSuccessStatus
|
||||||
|
resp.ErrorCode = code
|
||||||
|
if len(message) > 0 {
|
||||||
|
resp.ErrorInfo = message[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
return a.Ack(resp)
|
||||||
|
}
|
324
callback/types.go
Normal file
324
callback/types.go
Normal file
@ -0,0 +1,324 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Author:1711788888@qq.com
|
||||||
|
* @Date: 2021/5/27 16:36
|
||||||
|
* @Desc: Callback request struct defined.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package callback
|
||||||
|
|
||||||
|
import "git.echol.cn/loser/tencent-im/internal/types"
|
||||||
|
|
||||||
|
type (
|
||||||
|
BaseResp struct {
|
||||||
|
ErrorCode int `json:"ErrorCode"`
|
||||||
|
ErrorInfo string `json:"ErrorInfo"`
|
||||||
|
ActionStatus string `json:"ActionStatus"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// StateChange 状态变更回调
|
||||||
|
StateChange struct {
|
||||||
|
CallbackCommand string `json:"CallbackCommand"` // 回调命令
|
||||||
|
EventTime int64 `json:"EventTime"` // 触发本次回调的时间戳,单位为毫秒
|
||||||
|
Info struct {
|
||||||
|
UserId string `json:"To_Account"` // 用户 UserID
|
||||||
|
Action string `json:"Action"` // 用户上线或者下线的动作,Login 表示上线(TCP 建立),Logout 表示下线(TCP 断开),Disconnect 表示网络断开(TCP 断开)
|
||||||
|
Reason string `json:"Reason"` // 用户上下线触发的原因
|
||||||
|
} `json:"Info"` // 用户上下线的信息
|
||||||
|
KickedDevice []struct {
|
||||||
|
Platform string `json:"Platform"` // 被踢下线的设备的平台类型,可能的取值有"iOS", "Android", "Web", "Windows", "iPad", "Mac", "Linux"。
|
||||||
|
} `json:"KickedDevice"` // 此字段表示其他被踢下线的设备的信息
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeforeFriendAdd 添加好友之前回调
|
||||||
|
BeforeFriendAdd struct {
|
||||||
|
CallbackCommand string `json:"CallbackCommand"` // 回调命令
|
||||||
|
EventTime int64 `json:"EventTime"` // 触发本次回调的时间戳,单位为毫秒
|
||||||
|
RequesterUserId string `json:"Requester_Account"` // 请求发起方的 UserID
|
||||||
|
FromUserId string `json:"From_Account"` // 请求添加好友的用户的 UserID(A添加B为好友中的A)
|
||||||
|
AddType string `json:"AddType"` // 加好友方式(默认双向加好友方式,Add_Type_Single:表示单向加好友 Add_Type_Both:表示双向加好友)
|
||||||
|
ForceAddFlags int `json:"ForceAddFlags"` // 管理员强制加好友标记(1:表示强制加好友 0:表示常规加好友方式)
|
||||||
|
Friends []struct {
|
||||||
|
ToAccount string `json:"To_Account"` // 请求添加的用户的 UserID
|
||||||
|
Remark string `json:"Remark"` // From_Account 对 To_Account 设置的好友备注
|
||||||
|
GroupName string `json:"GroupName"` // From_Account 对 To_Account 设置的好友分组
|
||||||
|
AddSource string `json:"AddSource"` // 加好友来源
|
||||||
|
AddWording string `json:"AddWording"` // 加好友附言
|
||||||
|
} `json:"FriendItem"` // 加好友请求的参数
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeforeFriendAddResp 添加好友之前回调应答
|
||||||
|
BeforeFriendAddResp struct {
|
||||||
|
BaseResp
|
||||||
|
Results []*BeforeFriendAddResult `json:"ResultItem"` // App 后台的处理结果
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeforeFriendAddResult App后台的处理结果
|
||||||
|
BeforeFriendAddResult struct {
|
||||||
|
UserId string `json:"To_Account"` // (必填)请求添加的用户的 UserID
|
||||||
|
ResultCode int `json:"ResultCode"` // (必填)错误码:0表示允许加好友; 非0值表示不允许加好友; 如果不允许加好友,请将错误码设置在[38000, 39000]
|
||||||
|
ResultInfo string `json:"ResultInfo"` // (必填)错误信息
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeforeFriendResponse 添加好友回应之前回调
|
||||||
|
BeforeFriendResponse struct {
|
||||||
|
CallbackCommand string `json:"CallbackCommand"` // 回调命令
|
||||||
|
EventTime int64 `json:"EventTime"` // 触发本次回调的时间戳,单位为毫秒
|
||||||
|
RequesterUserId string `json:"Requester_Account"` // 请求发起方的 UserID
|
||||||
|
FromUserId string `json:"From_Account"` // 请求加好友回应的用户的 UserID
|
||||||
|
Friends []struct {
|
||||||
|
ToAccount string `json:"To_Account"` // 请求回应的用户的 UserID
|
||||||
|
Remark string `json:"Remark"` // From_Account 对 To_Account 设置的好友备注
|
||||||
|
TagName string `json:"TagName"` // From_Account 对 To_Account 设置的好友分组
|
||||||
|
ResponseAction string `json:"ResponseAction"` // 加好友回应方式,Response_Action_AgreeAndAdd 表示同意且添加对方为好友;Response_Action_Agree 表示同意对方加自己为好友;Response_Action_Reject 表示拒绝对方的加好友请求
|
||||||
|
} `json:"ResponseFriendItem"` // 加好友回应请求的参数
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeforeFriendResponseResp 添加好友之前回调应答
|
||||||
|
BeforeFriendResponseResp struct {
|
||||||
|
BaseResp
|
||||||
|
Results []*BeforeFriendAddResult `json:"ResultItem"` // App 后台的处理结果
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeforeFriendResponseResult App后台的处理结果
|
||||||
|
BeforeFriendResponseResult struct {
|
||||||
|
UserId string `json:"To_Account"` // (必填)请求添加的用户的 UserID
|
||||||
|
ResultCode int `json:"ResultCode"` // (必填)错误码:0表示允许加好友; 非0值表示不允许加好友; 如果不允许加好友,请将错误码设置在[38000, 39000]
|
||||||
|
ResultInfo string `json:"ResultInfo"` // (必填)错误信息
|
||||||
|
}
|
||||||
|
|
||||||
|
// AfterFriendAdd 添加好友之后
|
||||||
|
AfterFriendAdd struct {
|
||||||
|
CallbackCommand string `json:"CallbackCommand"` // 回调命令
|
||||||
|
ClientCmd string `json:"ClientCmd"` // 触发回调的命令字:加好友请求,合理的取值如下:friend_add、FriendAdd; 加好友回应,合理的取值如下:friend_response、FriendResponse
|
||||||
|
AdminUserId string `json:"Admin_Account"` // 如果当前请求是后台触发的加好友请求,则该字段被赋值为管理员帐号;否则为空
|
||||||
|
ForceFlag int `json:"ForceFlag"` // 管理员强制加好友标记:1 表示强制加好友;0 表示常规加好友方式
|
||||||
|
PairList []struct {
|
||||||
|
FromUserId string `json:"From_Account"` // From_Account 的好友表中增加了 To_Account
|
||||||
|
ToUserId string `json:"To_Account"` // To_Account 被增加到了 From_Account 的好友表中
|
||||||
|
InitiatorUserId string `json:"Initiator_Account"` // 发起加好友请求的用户的 UserID
|
||||||
|
} `json:"PairList"` // 成功添加的好友对
|
||||||
|
}
|
||||||
|
|
||||||
|
// AfterFriendDelete 删除好友之后回调
|
||||||
|
AfterFriendDelete struct {
|
||||||
|
CallbackCommand string `json:"CallbackCommand"` // 回调命令
|
||||||
|
PairList []struct {
|
||||||
|
FromUserId string `json:"From_Account"` // From_Account 的好友表中删除了 To_Account
|
||||||
|
ToUserId string `json:"To_Account"` // To_Account 从 From_Account 的好友表中删除
|
||||||
|
} `json:"PairList"` // 成功删除的好友
|
||||||
|
}
|
||||||
|
|
||||||
|
// AfterBlacklistAdd 添加黑名单之后回调
|
||||||
|
AfterBlacklistAdd struct {
|
||||||
|
CallbackCommand string `json:"CallbackCommand"` // 回调命令
|
||||||
|
PairList []struct {
|
||||||
|
FromUserId string `json:"From_Account"` // From_Account 的黑名单列表中添加了 To_Account
|
||||||
|
ToUserId string `json:"To_Account"` // To_Account 被加入到 From_Account 的黑名单列表中
|
||||||
|
} `json:"PairList"` // 成功添加的黑名单关系链对
|
||||||
|
}
|
||||||
|
|
||||||
|
// AfterBlacklistDelete 删除黑名单之后回调
|
||||||
|
AfterBlacklistDelete struct {
|
||||||
|
CallbackCommand string `json:"CallbackCommand"` // 回调命令
|
||||||
|
PairList []struct {
|
||||||
|
FromUserId string `json:"From_Account"` // From_Account 的黑名单列表中删除了 To_Account
|
||||||
|
ToUserId string `json:"To_Account"` // To_Account 从 From_Account 的黑名单列表中删除
|
||||||
|
} `json:"PairList"` // 成功删除的黑名单对
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeforePrivateMessageSend 发单聊消息之前回调
|
||||||
|
BeforePrivateMessageSend struct {
|
||||||
|
CallbackCommand string `json:"CallbackCommand"` // 回调命令
|
||||||
|
FromUserId string `json:"From_Account"` // 消息发送者 UserID
|
||||||
|
ToUserId string `json:"To_Account"` // 消息接收者 UserID
|
||||||
|
MsgSeq int `json:"MsgSeq"` // 消息序列号,用于标记该条消息(32位无符号整数)
|
||||||
|
MsgRandom int `json:"MsgRandom"` // 消息随机数,用于标记该条消息(32位无符号整数)
|
||||||
|
MsgTime int64 `json:"MsgTime"` // 消息的发送时间戳,单位为秒,单聊消息优先使用 MsgTime 进行排序,同一秒发送的消息则按 MsgSeq 排序,MsgSeq 值越大消息越靠后
|
||||||
|
MsgKey string `json:"MsgKey"` // 该条消息的唯一标识,可根据该标识进行 REST API 撤回单聊消息
|
||||||
|
OnlineOnlyFlag int `json:"OnlineOnlyFlag"` // 在线消息,为1,否则为0
|
||||||
|
MsgBody []*types.MsgBody `json:"MsgBody"` // 消息体
|
||||||
|
CloudCustomData string `json:"CloudCustomData"` // 消息自定义数据(云端保存,会发送到对端,程序卸载重装后还能拉取到)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeforePrivateMessageSendResp 发单聊消息之前回调应答
|
||||||
|
BeforePrivateMessageSendResp struct {
|
||||||
|
BaseResp
|
||||||
|
MsgBody []*types.MsgBody `json:"MsgBody,omitempty"` // (选填)App 修改之后的消息,如果没有,则默认使用用户发送的消息
|
||||||
|
CloudCustomData string `json:"CloudCustomData,omitempty"` // (选填)经过 App 修改之后的消息自定义数据(云端保存,会发送到对端,程序卸载重装后还能拉取到),即时通信 IM 后台将把修改后的消息发送给接收方
|
||||||
|
}
|
||||||
|
|
||||||
|
// AfterPrivateMessageSend 发单聊消息之后回调
|
||||||
|
AfterPrivateMessageSend struct {
|
||||||
|
CallbackCommand string `json:"CallbackCommand"` // 回调命令
|
||||||
|
FromUserId string `json:"From_Account"` // 消息发送者 UserID
|
||||||
|
ToUserId string `json:"To_Account"` // 消息接收者 UserID
|
||||||
|
MsgSeq int `json:"MsgSeq"` // 消息序列号,用于标记该条消息(32位无符号整数)
|
||||||
|
MsgRandom int `json:"MsgRandom"` // 消息随机数,用于标记该条消息(32位无符号整数)
|
||||||
|
MsgTime int64 `json:"MsgTime"` // 消息的发送时间戳,单位为秒,单聊消息优先使用 MsgTime 进行排序,同一秒发送的消息则按 MsgSeq 排序,MsgSeq 值越大消息越靠后
|
||||||
|
MsgKey string `json:"MsgKey"` // 该条消息的唯一标识,可根据该标识进行 REST API 撤回单聊消息
|
||||||
|
OnlineOnlyFlag int `json:"OnlineOnlyFlag"` // 在线消息,为1,否则为0
|
||||||
|
MsgBody []*types.MsgBody `json:"MsgBody"` // 消息体
|
||||||
|
CloudCustomData string `json:"CloudCustomData"` // 消息自定义数据(云端保存,会发送到对端,程序卸载重装后还能拉取到)
|
||||||
|
SendMsgResult int `json:"SendMsgResult"` // 该条消息的下发结果,0表示下发成功,非0表示下发失败
|
||||||
|
ErrorInfo string `json:"ErrorInfo"` // 该条消息下发失败的错误信息,若消息发送成功,则为"send msg succeed"
|
||||||
|
UnreadMsgNum int `json:"UnreadMsgNum"` // To_Account 未读的单聊消息总数量(包含所有的单聊会话)。若该条消息下发失败(例如被脏字过滤),该字段值为-1
|
||||||
|
}
|
||||||
|
|
||||||
|
// AfterPrivateMessageReport 单聊消息已读上报后回调
|
||||||
|
AfterPrivateMessageReport struct {
|
||||||
|
CallbackCommand string `json:"CallbackCommand"` // 回调命令
|
||||||
|
ReportUserId string `json:"Report_Account"` // 已读上报方 UserID
|
||||||
|
PeerUserId string `json:"Peer_Account"` // 会话对端 UserID
|
||||||
|
LastReadTime int64 `json:"LastReadTime"` // 已读时间
|
||||||
|
UnreadMsgNum int `json:"UnreadMsgNum"` // Report_Account 未读的单聊消息总数量(包含所有的单聊会话)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AfterPrivateMessageRevoke 单聊消息撤回后回调
|
||||||
|
AfterPrivateMessageRevoke struct {
|
||||||
|
CallbackCommand string `json:"CallbackCommand"` // 回调命令
|
||||||
|
FromUserId string `json:"From_Account"` // 消息发送者 UserID
|
||||||
|
ToUserId string `json:"To_Account"` // 消息接收者 UserID
|
||||||
|
MsgKey string `json:"MsgKey"` // 消息的唯一标识
|
||||||
|
UnreadMsgNum int `json:"UnreadMsgNum"` // To_Account 未读的单聊消息总数量(包含所有的单聊会话)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeforeGroupCreate 创建群组之前回调
|
||||||
|
BeforeGroupCreate struct {
|
||||||
|
CallbackCommand string `json:"CallbackCommand"` // 回调命令
|
||||||
|
OperatorUserId string `json:"Operator_Account"` // 操作者
|
||||||
|
OwnerUserId string `json:"Owner_Account"` // 群主
|
||||||
|
Type string `json:"Type"` // 群组类型
|
||||||
|
Name string `json:"Name"` // 请求创建的群组的名称
|
||||||
|
CreateGroupNum int `json:"CreateGroupNum"` // 该用户已创建的同类的群组个数
|
||||||
|
MemberList []struct {
|
||||||
|
UserId string `json:"Member_Account"` // 成员 UserID
|
||||||
|
} `json:"MemberList"` // 请求创建的群组的初始化成员列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// AfterGroupCreate 创建群组之后回调
|
||||||
|
AfterGroupCreate struct {
|
||||||
|
CallbackCommand string `json:"CallbackCommand"` // 回调命令
|
||||||
|
OperatorUserId string `json:"Operator_Account"` // 操作者
|
||||||
|
OwnerUserId string `json:"Owner_Account"` // 群主
|
||||||
|
GroupId string `json:"GroupId"` // 群ID
|
||||||
|
Type string `json:"Type"` // 群组类型
|
||||||
|
Name string `json:"Name"` // 请求创建的群组的名称
|
||||||
|
CreateGroupNum int `json:"CreateGroupNum"` // 该用户已创建的同类的群组个数
|
||||||
|
MemberList []struct {
|
||||||
|
UserId string `json:"Member_Account"` // 成员 UserID
|
||||||
|
} `json:"MemberList"` // 请求创建的群组的初始化成员列表
|
||||||
|
UserDefinedDataList []struct {
|
||||||
|
Key string `json:"Key"`
|
||||||
|
Value string `json:"Value"`
|
||||||
|
} `json:"UserDefinedDataList"` // 用户建群时的自定义字段
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeforeApplyJoinGroup 申请入群之前回调
|
||||||
|
BeforeApplyJoinGroup struct {
|
||||||
|
CallbackCommand string `json:"CallbackCommand"` // 回调命令
|
||||||
|
GroupId string `json:"GroupId"` // 群ID
|
||||||
|
Type string `json:"Type"` // 群组类型
|
||||||
|
RequestorUserId string `json:"Requestor_Account"` // 申请者
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeforeInviteJoinGroup 拉人入群之前回调
|
||||||
|
BeforeInviteJoinGroup struct {
|
||||||
|
CallbackCommand string `json:"CallbackCommand"` // 回调命令
|
||||||
|
GroupId string `json:"GroupId"` // 群ID
|
||||||
|
Type string `json:"Type"` // 群组类型
|
||||||
|
OperatorUserId string `json:"Operator_Account"` // 操作者
|
||||||
|
MemberList []struct {
|
||||||
|
UserId string `json:"Member_Account"` // 成员 UserID
|
||||||
|
} `json:"DestinationMembers"` // 要拉入群组的 UserID 集合
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeforeInviteJoinGroupResp 拉人入群之前回调应答
|
||||||
|
BeforeInviteJoinGroupResp struct {
|
||||||
|
BaseResp
|
||||||
|
RefusedMemberUserIds []string `json:"RefusedMembers_Account,omitempty"` // 拒绝加入的用户列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// AfterNewMemberJoinGroup 新成员入群之后回调
|
||||||
|
AfterNewMemberJoinGroup struct {
|
||||||
|
CallbackCommand string `json:"CallbackCommand"` // 回调命令
|
||||||
|
GroupId string `json:"GroupId"` // 群ID
|
||||||
|
Type string `json:"Type"` // 群组类型
|
||||||
|
JoinType string `json:"JoinType"` // 入群方式:Apply(申请入群);Invited(邀请入群)
|
||||||
|
OperatorUserId string `json:"Operator_Account"` // 操作者
|
||||||
|
MemberList []struct {
|
||||||
|
UserId string `json:"Member_Account"` // 成员 UserID
|
||||||
|
} `json:"NewMemberList"` // 新入群成员列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// AfterMemberExitGroup 群成员离开之后回调
|
||||||
|
AfterMemberExitGroup struct {
|
||||||
|
CallbackCommand string `json:"CallbackCommand"` // 回调命令
|
||||||
|
GroupId string `json:"GroupId"` // 群ID
|
||||||
|
Type string `json:"Type"` // 群组类型
|
||||||
|
ExitType string `json:"ExitType"` // 成员离开方式:Kicked-被踢;Quit-主动退群
|
||||||
|
OperatorUserId string `json:"Operator_Account"` // 操作者
|
||||||
|
MemberList []struct {
|
||||||
|
UserId string `json:"Member_Account"` // 成员 UserID
|
||||||
|
} `json:"ExitMemberList"` // 离开群的成员列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeforeGroupMessageSend 群内发言之前回调
|
||||||
|
BeforeGroupMessageSend struct {
|
||||||
|
CallbackCommand string `json:"CallbackCommand"` // 回调命令
|
||||||
|
GroupId string `json:"GroupId"` // 群ID
|
||||||
|
Type string `json:"Type"` // 群组类型
|
||||||
|
FromUserId string `json:"From_Account"` // 发送者
|
||||||
|
OperatorUserId string `json:"Operator_Account"` // 请求的发起者
|
||||||
|
OnlineOnlyFlag int `json:"OnlineOnlyFlag"` // 在线消息,为1,否则为0;直播群忽略此属性,为默认值0。
|
||||||
|
MsgRandom int `json:"Random"` // 随机数
|
||||||
|
MsgBody []*types.MsgBody `json:"MsgBody"` // 消息体
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeforeGroupMessageSendResp 群内发言之前回调应答
|
||||||
|
BeforeGroupMessageSendResp struct {
|
||||||
|
BaseResp
|
||||||
|
MsgBody []*types.MsgBody `json:"MsgBody,omitempty"` // (选填)App 修改之后的消息,如果没有,则默认使用用户发送的消息
|
||||||
|
}
|
||||||
|
|
||||||
|
// AfterGroupMessageSend 群内发言之后回调
|
||||||
|
AfterGroupMessageSend struct {
|
||||||
|
CallbackCommand string `json:"CallbackCommand"` // 回调命令
|
||||||
|
GroupId string `json:"GroupId"` // 群ID
|
||||||
|
Type string `json:"Type"` // 群组类型
|
||||||
|
FromUserId string `json:"From_Account"` // 发送者
|
||||||
|
OperatorUserId string `json:"Operator_Account"` // 请求的发起者
|
||||||
|
OnlineOnlyFlag int `json:"OnlineOnlyFlag"` // 在线消息,为1,否则为0;直播群忽略此属性,为默认值0。
|
||||||
|
MsgSeq int `json:"MsgSeq"` // 消息的序列号
|
||||||
|
MsgRandom int `json:"Random"` // 随机数
|
||||||
|
MsgTime int64 `json:"MsgTime"` // 消息的时间
|
||||||
|
MsgBody []*types.MsgBody `json:"MsgBody"` // 消息体
|
||||||
|
}
|
||||||
|
|
||||||
|
// AfterGroupFull 群组满员之后回调
|
||||||
|
AfterGroupFull struct {
|
||||||
|
CallbackCommand string `json:"CallbackCommand"` // 回调命令
|
||||||
|
GroupId string `json:"GroupId"` // 群ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// AfterGroupDestroyed 群组解散之后回调
|
||||||
|
AfterGroupDestroyed struct {
|
||||||
|
CallbackCommand string `json:"CallbackCommand"` // 回调命令
|
||||||
|
GroupId string `json:"GroupId"` // 群ID
|
||||||
|
Type string `json:"Type"` // 群组类型
|
||||||
|
Name string `json:"Name"` // 群组名称
|
||||||
|
OwnerUserId string `json:"Owner_Account"` // 操作者
|
||||||
|
MemberList []struct {
|
||||||
|
UserId string `json:"Member_Account"` // 成员 UserID
|
||||||
|
} `json:"MemberList"` // 被解散的群组中的成员
|
||||||
|
}
|
||||||
|
|
||||||
|
// AfterGroupInfoChanged 群组资料修改之后回调
|
||||||
|
AfterGroupInfoChanged struct {
|
||||||
|
CallbackCommand string `json:"CallbackCommand"` // 回调命令
|
||||||
|
GroupId string `json:"GroupId"` // 群ID
|
||||||
|
Type string `json:"Type"` // 群组类型
|
||||||
|
Notification string `json:"Notification"` // 修改后的群公告
|
||||||
|
OperatorUserId string `json:"Operator_Account"` // 请求的发起者
|
||||||
|
}
|
||||||
|
)
|
63
example/main.go
Normal file
63
example/main.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Author:1711788888@qq.com
|
||||||
|
* @Date: 2021/8/31 15:14
|
||||||
|
* @Desc: TODO
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"git.echol.cn/loser/tencent-im"
|
||||||
|
"git.echol.cn/loser/tencent-im/account"
|
||||||
|
"git.echol.cn/loser/tencent-im/callback"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
tim := im.NewIM(&im.Options{
|
||||||
|
AppId: 1400579830, // 无效的AppId,请勿直接使用
|
||||||
|
AppSecret: "0d2a321b087fdb8fd5ed5ea14fe0489139086eb1b03541283fc9feeab8f2bfd3", // 无效的AppSecret,请勿直接使用
|
||||||
|
UserId: "administrator", // 管理员用户账号,请在腾讯云IM后台设置管理账号
|
||||||
|
})
|
||||||
|
|
||||||
|
// 导入账号
|
||||||
|
if err := tim.Account().ImportAccount(&account.Account{
|
||||||
|
UserId: "test1",
|
||||||
|
Nickname: "测试账号1",
|
||||||
|
FaceUrl: "https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png",
|
||||||
|
}); err != nil {
|
||||||
|
if e, ok := err.(im.Error); ok {
|
||||||
|
fmt.Println(fmt.Sprintf("import account failed, code:%d, message:%s.", e.Code(), e.Message()))
|
||||||
|
} else {
|
||||||
|
fmt.Println(fmt.Sprintf("import account failed:%s.", err.Error()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("import account success.")
|
||||||
|
|
||||||
|
// 注册回调事件
|
||||||
|
tim.Callback().Register(callback.EventAfterFriendAdd, func(ack callback.Ack, data interface{}) {
|
||||||
|
fmt.Printf("%+v", data.(callback.AfterFriendAdd))
|
||||||
|
_ = ack.AckSuccess(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 注册回调事件
|
||||||
|
tim.Callback().Register(callback.EventAfterFriendDelete, func(ack callback.Ack, data interface{}) {
|
||||||
|
fmt.Printf("%+v", data.(callback.AfterFriendDelete))
|
||||||
|
_ = ack.AckSuccess(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 开启监听
|
||||||
|
http.HandleFunc("/callback", func(writer http.ResponseWriter, request *http.Request) {
|
||||||
|
tim.Callback().Listen(writer, request)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 启动服务器
|
||||||
|
if err := http.ListenAndServe(":8080", nil); err != nil {
|
||||||
|
log.Fatal("ListenAndServe: ", err)
|
||||||
|
}
|
||||||
|
}
|
7
go.mod
Normal file
7
go.mod
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
module git.echol.cn/loser/tencent-im
|
||||||
|
|
||||||
|
go 1.18
|
||||||
|
|
||||||
|
require git.echol.cn/loser/http v0.0.3
|
||||||
|
|
||||||
|
require github.com/dobyte/http v0.0.2 // indirect
|
1315
group/api.go
Normal file
1315
group/api.go
Normal file
File diff suppressed because it is too large
Load Diff
210
group/filter.go
Normal file
210
group/filter.go
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Author:1711788888@qq.com
|
||||||
|
* @Date: 2021/9/8 10:17
|
||||||
|
* @Desc: 群组响应过滤器
|
||||||
|
*/
|
||||||
|
|
||||||
|
package group
|
||||||
|
|
||||||
|
type (
|
||||||
|
// BaseInfoField 群基础信息字段
|
||||||
|
BaseInfoField string
|
||||||
|
|
||||||
|
// MemberInfoField 群成员信息字段
|
||||||
|
MemberInfoField string
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
BaseFieldGroupId BaseInfoField = "GroupId" // 群组的唯一标识
|
||||||
|
BaseFieldType BaseInfoField = "Type" // 群组类型
|
||||||
|
BaseFieldName BaseInfoField = "Name" // 群组名称
|
||||||
|
BaseFieldIntroduction BaseInfoField = "Introduction" // 群组简介
|
||||||
|
BaseFieldNotification BaseInfoField = "Notification" // 群组公告
|
||||||
|
BaseFieldAvatar BaseInfoField = "FaceUrl" // 群组头像URL
|
||||||
|
BaseFieldOwner BaseInfoField = "Owner_Account" // 群主ID
|
||||||
|
BaseFieldCreateTime BaseInfoField = "CreateTime" // 群组的创建时间
|
||||||
|
BaseFieldInfoSeq BaseInfoField = "InfoSeq" // 群资料变更次数
|
||||||
|
BaseFieldLastInfoTime BaseInfoField = "LastInfoTime" // 群组最后一次信息变更时间
|
||||||
|
BaseFieldLastMsgTime BaseInfoField = "LastMsgTime" // 群组内最后发消息的时间
|
||||||
|
BaseFieldNextMsgSeq BaseInfoField = "NextMsgSeq" // 群内下一条消息的Seq
|
||||||
|
BaseFieldMemberNum BaseInfoField = "MemberNum" // 当前成员数量
|
||||||
|
BaseFieldMaxMemberNum BaseInfoField = "MaxMemberNum" // 最大成员数量
|
||||||
|
BaseFieldApplyJoinOption BaseInfoField = "ApplyJoinOption" // 申请加群选项
|
||||||
|
|
||||||
|
MemberFieldUserId MemberInfoField = "Member_Account" // 群成员ID
|
||||||
|
MemberFieldRole MemberInfoField = "Role" // 群内身份
|
||||||
|
MemberFieldJoinTime MemberInfoField = "JoinTime" // 入群时间
|
||||||
|
MemberFieldMsgSeq MemberInfoField = "MsgSeq" // 该成员当前已读消息Seq
|
||||||
|
MemberFieldMsgFlag MemberInfoField = "MsgFlag" // 消息接收选项
|
||||||
|
MemberFieldLastSendMsgTime MemberInfoField = "LastSendMsgTime" // 最后发送消息的时间
|
||||||
|
MemberFieldNameCard MemberInfoField = "NameCard" // 群名片
|
||||||
|
)
|
||||||
|
|
||||||
|
type Filter struct {
|
||||||
|
baseInfo map[string]bool
|
||||||
|
memberInfo map[string]bool
|
||||||
|
memberRole map[string]bool
|
||||||
|
groupCustomData map[string]bool
|
||||||
|
memberCustomData map[string]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddBaseInfoFilter 添加基础信息过滤器
|
||||||
|
func (f *Filter) AddBaseInfoFilter(field BaseInfoField) {
|
||||||
|
if f.baseInfo == nil {
|
||||||
|
f.baseInfo = make(map[string]bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.baseInfo[string(field)] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemBaseInfoFilter 移除基础信息过滤器
|
||||||
|
func (f *Filter) RemBaseInfoFilter(field BaseInfoField) {
|
||||||
|
if f.baseInfo == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(f.baseInfo, string(field))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllBaseInfoFilterFields 获取所有基础信息过滤器字段
|
||||||
|
func (f *Filter) GetAllBaseInfoFilterFields() (filters []string) {
|
||||||
|
if f.baseInfo == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
filters = make([]string, 0, len(f.baseInfo))
|
||||||
|
for k, _ := range f.baseInfo {
|
||||||
|
filters = append(filters, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddMemberInfoFilter 添加成员信息过滤器
|
||||||
|
func (f *Filter) AddMemberInfoFilter(field MemberInfoField) {
|
||||||
|
if f.memberInfo == nil {
|
||||||
|
f.memberInfo = make(map[string]bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.memberInfo[string(field)] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemMemberInfoFilter 移除成员信息过滤器
|
||||||
|
func (f *Filter) RemMemberInfoFilter(field MemberInfoField) {
|
||||||
|
if f.memberInfo == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(f.memberInfo, string(field))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllMemberInfoFilterFields 获取所有成员信息过滤器字段
|
||||||
|
func (f *Filter) GetAllMemberInfoFilterFields() (filters []string) {
|
||||||
|
if f.memberInfo == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
filters = make([]string, 0, len(f.memberInfo))
|
||||||
|
for k, _ := range f.memberInfo {
|
||||||
|
filters = append(filters, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddMemberRoleFilter 添加群成员角色过滤器
|
||||||
|
func (f *Filter) AddMemberRoleFilter(field string) {
|
||||||
|
if f.memberRole == nil {
|
||||||
|
f.memberRole = make(map[string]bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.memberRole[field] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemMemberRoleFilter 移除群成员角色过滤器
|
||||||
|
func (f *Filter) RemMemberRoleFilter(field string) {
|
||||||
|
if f.memberRole == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(f.memberRole, field)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllMemberRoleFilterValues 获取所有群成员角色过滤器值
|
||||||
|
func (f *Filter) GetAllMemberRoleFilterValues() (filters []string) {
|
||||||
|
if f.memberRole == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
filters = make([]string, 0, len(f.memberRole))
|
||||||
|
for k, _ := range f.memberRole {
|
||||||
|
filters = append(filters, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddGroupCustomDataFilter 添加群自定义数据过滤器
|
||||||
|
func (f *Filter) AddGroupCustomDataFilter(field string) {
|
||||||
|
if f.groupCustomData == nil {
|
||||||
|
f.groupCustomData = make(map[string]bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.groupCustomData[field] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemGroupCustomDataFilter 移除群自定义数据过滤器
|
||||||
|
func (f *Filter) RemGroupCustomDataFilter(field string) {
|
||||||
|
if f.groupCustomData == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(f.groupCustomData, field)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllGroupCustomDataFilterFields 获取所有群自定义数据过滤器字段
|
||||||
|
func (f *Filter) GetAllGroupCustomDataFilterFields() (filters []string) {
|
||||||
|
if f.groupCustomData == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
filters = make([]string, 0, len(f.groupCustomData))
|
||||||
|
for k, _ := range f.groupCustomData {
|
||||||
|
filters = append(filters, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddMemberCustomDataFilter 添加群成员自定义数据过滤器
|
||||||
|
func (f *Filter) AddMemberCustomDataFilter(field string) {
|
||||||
|
if f.memberCustomData == nil {
|
||||||
|
f.memberCustomData = make(map[string]bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.memberCustomData[field] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemMemberCustomDataFilter 移除群成员自定义数据过滤器
|
||||||
|
func (f *Filter) RemMemberCustomDataFilter(field string) {
|
||||||
|
if f.memberCustomData == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(f.memberCustomData, field)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllMemberCustomDataFilterFields 获取所有群成员自定义数据过滤器字段
|
||||||
|
func (f *Filter) GetAllMemberCustomDataFilterFields() (filters []string) {
|
||||||
|
if f.memberCustomData == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
filters = make([]string, 0, len(f.memberCustomData))
|
||||||
|
for k, _ := range f.memberCustomData {
|
||||||
|
filters = append(filters, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
383
group/group.go
Normal file
383
group/group.go
Normal file
@ -0,0 +1,383 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Author:1711788888@qq.com
|
||||||
|
* @Date: 2021/9/7 17:11
|
||||||
|
* @Desc: TODO
|
||||||
|
*/
|
||||||
|
|
||||||
|
package group
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/core"
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errNotSetGroupType = core.NewError(enum.InvalidParamsCode, "group type is not set")
|
||||||
|
errNotSetGroupName = core.NewError(enum.InvalidParamsCode, "group name is not set")
|
||||||
|
errGroupNameTooLong = core.NewError(enum.InvalidParamsCode, "group name is too long")
|
||||||
|
errInvalidGroupType = core.NewError(enum.InvalidParamsCode, "invalid group type")
|
||||||
|
errGroupIntroductionTooLong = core.NewError(enum.InvalidParamsCode, "group introduction is too long")
|
||||||
|
errGroupNotificationTooLong = core.NewError(enum.InvalidParamsCode, "group notification is too long")
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// Type 群类型
|
||||||
|
Type string
|
||||||
|
|
||||||
|
// ApplyJoinOption 申请加群处理方式
|
||||||
|
ApplyJoinOption string
|
||||||
|
|
||||||
|
// ShutUpStatus 全员禁言状态
|
||||||
|
ShutUpStatus string
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
TypePublic Type = "Public" // Public(陌生人社交群)
|
||||||
|
TypePrivate Type = "Private" // Private(即 Work,好友工作群)
|
||||||
|
TypeChatRoom Type = "ChatRoom" // ChatRoom(即 Meeting,会议群)
|
||||||
|
TypeLiveRoom Type = "AVChatRoom" // AVChatRoom(直播群)
|
||||||
|
|
||||||
|
ApplyJoinOptionFreeAccess ApplyJoinOption = "FreeAccess" // 自由加入
|
||||||
|
ApplyJoinOptionNeedPermission ApplyJoinOption = "NeedPermission" // 需要验证
|
||||||
|
ApplyJoinOptionDisableApply ApplyJoinOption = "DisableApply" // 禁止加群
|
||||||
|
|
||||||
|
ShutUpStatusOn ShutUpStatus = "On" // 开启
|
||||||
|
ShutUpStatusOff ShutUpStatus = "Off" // 关闭
|
||||||
|
)
|
||||||
|
|
||||||
|
type Group struct {
|
||||||
|
err error
|
||||||
|
id string // 群ID
|
||||||
|
name string // 群名称
|
||||||
|
groupType Type // 群类型
|
||||||
|
owner string // 群主ID
|
||||||
|
introduction string // 群简介
|
||||||
|
notification string // 群公告
|
||||||
|
avatar string // 群头像
|
||||||
|
memberNum uint // 群成员数
|
||||||
|
maxMemberNum uint // 最大群成员数量
|
||||||
|
applyJoinOption string // 申请加群处理方式
|
||||||
|
members []*Member // 群成员
|
||||||
|
customData map[string]interface{} // 群自定义数据
|
||||||
|
createTime int64 // 群创建时间
|
||||||
|
lastInfoTime int64 // 最后群资料变更时间
|
||||||
|
lastMsgTime int64 // 群内最后一条消息的时间
|
||||||
|
nextMsgSeq int // 群内下一条消息的Seq
|
||||||
|
shutUpStatus string // 群全员禁言状态
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGroup(id ...string) *Group {
|
||||||
|
group := &Group{}
|
||||||
|
if len(id) > 0 {
|
||||||
|
group.SetGroupId(id[0])
|
||||||
|
}
|
||||||
|
return group
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetGroupId 设置群ID
|
||||||
|
func (g *Group) SetGroupId(id string) {
|
||||||
|
g.id = id
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGroupId 获取群ID
|
||||||
|
func (g *Group) GetGroupId() string {
|
||||||
|
return g.id
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetOwner 设置群主ID
|
||||||
|
func (g *Group) SetOwner(owner string) {
|
||||||
|
g.owner = owner
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOwner 获取群主ID
|
||||||
|
func (g *Group) GetOwner() string {
|
||||||
|
return g.owner
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetName 设置群名称
|
||||||
|
func (g *Group) SetName(name string) {
|
||||||
|
g.name = name
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetName 获取群名称
|
||||||
|
func (g *Group) GetName() string {
|
||||||
|
return g.name
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetGroupType 设置群类型
|
||||||
|
func (g *Group) SetGroupType(groupType Type) {
|
||||||
|
g.groupType = groupType
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGroupType 获取群类型
|
||||||
|
func (g *Group) GetGroupType() Type {
|
||||||
|
return g.groupType
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetIntroduction 设置群简介
|
||||||
|
func (g *Group) SetIntroduction(introduction string) {
|
||||||
|
g.introduction = introduction
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIntroduction 获取群简介
|
||||||
|
func (g *Group) GetIntroduction() string {
|
||||||
|
return g.introduction
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNotification 设置群公告
|
||||||
|
func (g *Group) SetNotification(notification string) {
|
||||||
|
g.notification = notification
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNotification 获取群公告
|
||||||
|
func (g *Group) GetNotification() string {
|
||||||
|
return g.notification
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAvatar 设置群头像
|
||||||
|
func (g *Group) SetAvatar(avatar string) {
|
||||||
|
g.avatar = avatar
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAvatar 获取群头像
|
||||||
|
func (g *Group) GetAvatar() string {
|
||||||
|
return g.avatar
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMaxMemberNum 设置最大群成员数量
|
||||||
|
func (g *Group) SetMaxMemberNum(maxMemberNum uint) {
|
||||||
|
g.maxMemberNum = maxMemberNum
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMaxMemberNum 获取最大群成员数量
|
||||||
|
func (g *Group) GetMaxMemberNum() uint {
|
||||||
|
return g.maxMemberNum
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMemberNum 获取群成员数
|
||||||
|
func (g *Group) GetMemberNum() uint {
|
||||||
|
return g.memberNum
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetApplyJoinOption 设置申请加群处理方式
|
||||||
|
func (g *Group) SetApplyJoinOption(applyJoinOption ApplyJoinOption) {
|
||||||
|
g.applyJoinOption = string(applyJoinOption)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetApplyJoinOption 获取申请加群处理方式
|
||||||
|
func (g *Group) GetApplyJoinOption() string {
|
||||||
|
return g.applyJoinOption
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddMembers 添加群成员
|
||||||
|
func (g *Group) AddMembers(member ...*Member) {
|
||||||
|
if g.members == nil {
|
||||||
|
g.members = make([]*Member, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
g.members = append(g.members, member...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMembers 设置群成员
|
||||||
|
func (g *Group) SetMembers(member ...*Member) {
|
||||||
|
if g.members != nil {
|
||||||
|
g.members = g.members[0:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
g.AddMembers(member...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCustomData 设置自定义数据
|
||||||
|
func (g *Group) SetCustomData(name string, value interface{}) {
|
||||||
|
if g.customData == nil {
|
||||||
|
g.customData = make(map[string]interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
g.customData[name] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCustomData 获取自定义数据
|
||||||
|
func (g *Group) GetCustomData(name string) (val interface{}, exist bool) {
|
||||||
|
if g.customData == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val, exist = g.customData[name]
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllCustomData 获取所有自定义数据
|
||||||
|
func (g *Group) GetAllCustomData() map[string]interface{} {
|
||||||
|
return g.customData
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMembers 获取群成员
|
||||||
|
func (g *Group) GetMembers() []*Member {
|
||||||
|
return g.members
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGroupCreateTime 获取群创建时间
|
||||||
|
func (g *Group) GetGroupCreateTime() time.Time {
|
||||||
|
return time.Unix(g.createTime, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLastInfoTime 获取最后群资料变更时间
|
||||||
|
func (g *Group) GetLastInfoTime() time.Time {
|
||||||
|
return time.Unix(g.lastInfoTime, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLastMsgTime 获取群内最后一条消息的时间
|
||||||
|
func (g *Group) GetLastMsgTime() time.Time {
|
||||||
|
return time.Unix(g.lastMsgTime, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNextMsgSeq 获取群内下一条消息的Seq
|
||||||
|
func (g *Group) GetNextMsgSeq() int {
|
||||||
|
return g.nextMsgSeq
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetShutUpStatus 设置全员禁言状态
|
||||||
|
func (g *Group) SetShutUpStatus(shutUpStatus ShutUpStatus) {
|
||||||
|
g.shutUpStatus = string(shutUpStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetShutUpStatus 获取群全员禁言状态
|
||||||
|
func (g *Group) GetShutUpStatus() string {
|
||||||
|
return g.shutUpStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCreateTime 设置群组创建时间
|
||||||
|
func (g *Group) SetCreateTime(createTime int64) {
|
||||||
|
g.createTime = createTime
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCreateTime 获取群组创建时间
|
||||||
|
func (g *Group) GetCreateTime() int64 {
|
||||||
|
return g.createTime
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValid 检测用户是否有效
|
||||||
|
func (g *Group) IsValid() bool {
|
||||||
|
return g.err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetError 获取异常错误
|
||||||
|
func (g *Group) GetError() error {
|
||||||
|
return g.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置异常错误
|
||||||
|
func (g *Group) setError(code int, message string) {
|
||||||
|
if code != enum.SuccessCode {
|
||||||
|
g.err = core.NewError(code, message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测创建错误
|
||||||
|
func (g *Group) checkCreateError() (err error) {
|
||||||
|
if err = g.checkTypeArgError(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = g.checkNameArgError(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = g.checkIntroductionArgError(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = g.checkNotificationArgError(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测导入错误
|
||||||
|
func (g *Group) checkImportError() (err error) {
|
||||||
|
if err = g.checkTypeArgError(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = g.checkNameArgError(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = g.checkIntroductionArgError(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = g.checkNotificationArgError(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测更新错误
|
||||||
|
func (g *Group) checkUpdateError() (err error) {
|
||||||
|
if err = g.checkNameArgError(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = g.checkIntroductionArgError(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = g.checkNotificationArgError(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测群名称参数错误
|
||||||
|
func (g *Group) checkNameArgError() error {
|
||||||
|
if g.name == "" {
|
||||||
|
return errNotSetGroupName
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(g.name) > 30 {
|
||||||
|
return errGroupNameTooLong
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测群类型参数错误
|
||||||
|
func (g *Group) checkTypeArgError() error {
|
||||||
|
if g.groupType == "" {
|
||||||
|
return errNotSetGroupType
|
||||||
|
}
|
||||||
|
|
||||||
|
switch Type(g.groupType) {
|
||||||
|
case TypePublic, TypePrivate, TypeChatRoom, TypeLiveRoom:
|
||||||
|
default:
|
||||||
|
return errInvalidGroupType
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测群简介参数错误
|
||||||
|
func (g *Group) checkIntroductionArgError() error {
|
||||||
|
if len(g.introduction) > 240 {
|
||||||
|
return errGroupIntroductionTooLong
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测群公告参数错误
|
||||||
|
func (g *Group) checkNotificationArgError() error {
|
||||||
|
if len(g.notification) > 300 {
|
||||||
|
return errGroupNotificationTooLong
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
160
group/member.go
Normal file
160
group/member.go
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Author:1711788888@qq.com
|
||||||
|
* @Date: 2021/9/7 18:06
|
||||||
|
* @Desc: TODO
|
||||||
|
*/
|
||||||
|
|
||||||
|
package group
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errNotSetUserId = errors.New("member's userid is not set")
|
||||||
|
|
||||||
|
type (
|
||||||
|
// MsgFlag 消息接收选项
|
||||||
|
MsgFlag string
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MsgFlagAcceptAndNotify MsgFlag = "AcceptAndNotify" // 接收并提示
|
||||||
|
MsgFlagAcceptNotNotify MsgFlag = "AcceptNotNotify" // 接收不提示(不会触发 APNs 远程推送)
|
||||||
|
MsgFlagDiscard MsgFlag = "Discard" // 屏蔽群消息(不会向客户端推送消息)
|
||||||
|
)
|
||||||
|
|
||||||
|
type Member struct {
|
||||||
|
userId string // 成员ID
|
||||||
|
role string // 群内身份
|
||||||
|
joinTime int64 // 加入时间
|
||||||
|
nameCard string // 群名片
|
||||||
|
msgSeq int // 成员当前已读消息Seq
|
||||||
|
msgFlag MsgFlag // 消息接收选项
|
||||||
|
lastSendMsgTime int64 // 最后发送消息的时间
|
||||||
|
shutUpUntil *int64 // 需禁言时间,单位为秒,0表示取消禁言
|
||||||
|
unreadMsgNum int // 未读入群成员的未读消息计数
|
||||||
|
customData map[string]interface{} // 自定义数据
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMember(userId ...string) *Member {
|
||||||
|
member := &Member{}
|
||||||
|
if len(userId) > 0 {
|
||||||
|
member.SetUserId(userId[0])
|
||||||
|
}
|
||||||
|
return member
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUserId 设置群成员ID
|
||||||
|
func (m *Member) SetUserId(userId string) {
|
||||||
|
m.userId = userId
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserId 获取群成员ID
|
||||||
|
func (m *Member) GetUserId() string {
|
||||||
|
return m.userId
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRole 设置群内身份
|
||||||
|
func (m *Member) SetRole(role string) {
|
||||||
|
m.role = role
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRole 获取群内身份
|
||||||
|
func (m *Member) GetRole() string {
|
||||||
|
return m.role
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetJoinTime 设置加入时间
|
||||||
|
func (m *Member) SetJoinTime(joinTime time.Time) {
|
||||||
|
m.joinTime = joinTime.Unix()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetJoinTime 获取加入时间
|
||||||
|
func (m *Member) GetJoinTime() time.Time {
|
||||||
|
return time.Unix(m.joinTime, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNameCard 设置群名片
|
||||||
|
func (m *Member) SetNameCard(nameCard string) {
|
||||||
|
m.nameCard = nameCard
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNameCard 获取群名片
|
||||||
|
func (m *Member) GetNameCard() string {
|
||||||
|
return m.nameCard
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMsgSeq 获取成员当前已读消息Seq
|
||||||
|
func (m *Member) GetMsgSeq() int {
|
||||||
|
return m.msgSeq
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMsgFlag 设置消息接收选项
|
||||||
|
func (m *Member) SetMsgFlag(msgFlag MsgFlag) {
|
||||||
|
m.msgFlag = msgFlag
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMsgFlag 获取消息接收选项
|
||||||
|
func (m *Member) GetMsgFlag() MsgFlag {
|
||||||
|
return m.msgFlag
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetShutUpUntil 设置需禁言时间,单位为秒,0表示取消禁言
|
||||||
|
func (m *Member) SetShutUpUntil(shutUpUntil int64) {
|
||||||
|
m.shutUpUntil = &shutUpUntil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetShutUpUntil 获取需禁言时间,单位为秒,0表示取消禁言
|
||||||
|
func (m *Member) GetShutUpUntil() int64 {
|
||||||
|
if m.shutUpUntil == nil {
|
||||||
|
return 0
|
||||||
|
} else {
|
||||||
|
return *m.shutUpUntil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUnreadMsgNum 设置成员的未读消息计数
|
||||||
|
func (m *Member) SetUnreadMsgNum(unreadMsgNum int) {
|
||||||
|
m.unreadMsgNum = unreadMsgNum
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUnreadMsgNum 获取成员的未读消息计数
|
||||||
|
func (m *Member) GetUnreadMsgNum() int {
|
||||||
|
return m.unreadMsgNum
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCustomData 设置自定义数据
|
||||||
|
func (m *Member) SetCustomData(name string, value interface{}) {
|
||||||
|
if m.customData == nil {
|
||||||
|
m.customData = make(map[string]interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
m.customData[name] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCustomData 获取自定义数据
|
||||||
|
func (m *Member) GetCustomData(name string) (val interface{}, exist bool) {
|
||||||
|
if m.customData == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val, exist = m.customData[name]
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllCustomData 获取所有自定义数据
|
||||||
|
func (m *Member) GetAllCustomData() map[string]interface{} {
|
||||||
|
return m.customData
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测参数错误
|
||||||
|
func (m *Member) checkError() (err error) {
|
||||||
|
if m.userId == "" {
|
||||||
|
return errNotSetUserId
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
221
group/message.go
Normal file
221
group/message.go
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Author:1711788888@qq.com
|
||||||
|
* @Date: 2021/8/31 18:04
|
||||||
|
* @Desc: 私聊消息实体
|
||||||
|
*/
|
||||||
|
|
||||||
|
package group
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/entity"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errNotSetSender = errors.New("message's sender not set")
|
||||||
|
errNotSetSendTime = errors.New("message's send time not set")
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// MsgOnlineOnlyFlag 只发送在线成员标识
|
||||||
|
MsgOnlineOnlyFlag int
|
||||||
|
|
||||||
|
// MsgPriority 消息优先级
|
||||||
|
MsgPriority string
|
||||||
|
|
||||||
|
// MsgStatus 消息状态
|
||||||
|
MsgStatus int
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MsgOnlineOnlyFlagNo MsgOnlineOnlyFlag = 0 // 发送所有成员
|
||||||
|
MsgOnlineOnlyFlagYes MsgOnlineOnlyFlag = 1 // 仅发送在线成员
|
||||||
|
|
||||||
|
MsgPriorityHigh MsgPriority = "High" // 高优先级消息
|
||||||
|
MsgPriorityNormal MsgPriority = "Normal" // 普通优先级消息
|
||||||
|
MsgPriorityLow MsgPriority = "Low" // 低优先级消息
|
||||||
|
MsgPriorityLowest MsgPriority = "Lowest" // 最低优先级消息
|
||||||
|
|
||||||
|
MsgStatusNormal MsgStatus = 0 // 正常消息
|
||||||
|
MsgStatusInvalid MsgStatus = 1 // 被删除或者消息过期的消息
|
||||||
|
MsgStatusRevoked MsgStatus = 2 // 被撤回的消息
|
||||||
|
|
||||||
|
AtAllMembersFlag = "@all" // @所有成员的标识
|
||||||
|
)
|
||||||
|
|
||||||
|
type Message struct {
|
||||||
|
entity.Message
|
||||||
|
priority MsgPriority // 消息的优先级
|
||||||
|
onlineOnlyFlag MsgOnlineOnlyFlag // 仅发送在线成员标识
|
||||||
|
sendTime int64 // 消息发送时间
|
||||||
|
timestamp int64 // 消息时间戳,UNIX 时间戳(单位:秒)
|
||||||
|
seq int // 消息序列号
|
||||||
|
status MsgStatus // 消息状态
|
||||||
|
customData interface{} // 自定义数据
|
||||||
|
sendControls map[string]bool // 发送消息控制
|
||||||
|
callbackControls map[string]bool // 禁用回调
|
||||||
|
atMembers map[string]bool // @用户
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMessage() *Message {
|
||||||
|
return &Message{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPriority 设置消息优先级
|
||||||
|
func (m *Message) SetPriority(priority MsgPriority) {
|
||||||
|
m.priority = priority
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPriority 获取消息优先级
|
||||||
|
func (m *Message) GetPriority() MsgPriority {
|
||||||
|
return m.priority
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCustomData 设置自定义数据
|
||||||
|
func (m *Message) SetCustomData(data interface{}) {
|
||||||
|
m.customData = data
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCustomData 获取自定义数据
|
||||||
|
func (m *Message) GetCustomData() interface{} {
|
||||||
|
return m.customData
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetOnlineOnlyFlag 设置仅发送在线成员标识
|
||||||
|
func (m *Message) SetOnlineOnlyFlag(flag MsgOnlineOnlyFlag) {
|
||||||
|
m.onlineOnlyFlag = flag
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOnlineOnlyFlag 获取仅发送在线成员标识
|
||||||
|
func (m *Message) GetOnlineOnlyFlag() MsgOnlineOnlyFlag {
|
||||||
|
return m.onlineOnlyFlag
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSendTime 设置发送时间
|
||||||
|
func (m *Message) SetSendTime(sendTime int64) {
|
||||||
|
m.sendTime = sendTime
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSendTime 获取发送时间
|
||||||
|
func (m *Message) GetSendTime() int64 {
|
||||||
|
return m.sendTime
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStatus 获取消息状态
|
||||||
|
func (m *Message) GetStatus() MsgStatus {
|
||||||
|
return m.status
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetForbidBeforeSendMsgCallback 设置禁止发消息前回调
|
||||||
|
func (m *Message) SetForbidBeforeSendMsgCallback() {
|
||||||
|
if m.callbackControls == nil {
|
||||||
|
m.callbackControls = make(map[string]bool, 0)
|
||||||
|
}
|
||||||
|
m.callbackControls["ForbidBeforeSendMsgCallback"] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetForbidAfterSendMsgCallback 设置禁止发消息后回调
|
||||||
|
func (m *Message) SetForbidAfterSendMsgCallback() {
|
||||||
|
if m.callbackControls == nil {
|
||||||
|
m.callbackControls = make(map[string]bool, 0)
|
||||||
|
}
|
||||||
|
m.callbackControls["ForbidAfterSendMsgCallback"] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetForbidCallbackControl 获取消息回调禁止开关
|
||||||
|
func (m *Message) GetForbidCallbackControl() (controls []string) {
|
||||||
|
if m.callbackControls != nil {
|
||||||
|
if n := len(m.callbackControls); n > 0 {
|
||||||
|
controls = make([]string, 0, n)
|
||||||
|
for k := range m.callbackControls {
|
||||||
|
controls = append(controls, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNoUnread 设置该条消息不计入未读数
|
||||||
|
func (m *Message) SetNoUnread() {
|
||||||
|
if m.sendControls == nil {
|
||||||
|
m.sendControls = make(map[string]bool, 0)
|
||||||
|
}
|
||||||
|
m.sendControls["NoUnread"] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNoLastMsg 设置该条消息不更新会话列表
|
||||||
|
func (m *Message) SetNoLastMsg() {
|
||||||
|
if m.sendControls == nil {
|
||||||
|
m.sendControls = make(map[string]bool, 0)
|
||||||
|
}
|
||||||
|
m.sendControls["NoLastMsg"] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSendMsgControl 获取消息发送控制选项
|
||||||
|
func (m *Message) GetSendMsgControl() (controls []string) {
|
||||||
|
if m.sendControls != nil {
|
||||||
|
if n := len(m.sendControls); n > 0 {
|
||||||
|
controls = make([]string, 0, n)
|
||||||
|
for k := range m.sendControls {
|
||||||
|
controls = append(controls, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// AtMembers @某个成员
|
||||||
|
func (m *Message) AtMembers(userId ...string) {
|
||||||
|
if m.atMembers == nil {
|
||||||
|
m.atMembers = make(map[string]bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, id := range userId {
|
||||||
|
m.atMembers[id] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AtAllMembers @所有成员
|
||||||
|
func (m *Message) AtAllMembers() {
|
||||||
|
m.AtMembers(AtAllMembersFlag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearAtMembers 清空所有的的@成员
|
||||||
|
func (m *Message) ClearAtMembers() {
|
||||||
|
m.atMembers = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTimestamp 获取消息的时间戳
|
||||||
|
func (m *Message) GetTimestamp() int64 {
|
||||||
|
return m.timestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测发送错误
|
||||||
|
func (m *Message) checkSendError() (err error) {
|
||||||
|
if err = m.CheckBodyArgError(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测导入错误
|
||||||
|
func (m *Message) checkImportError() (err error) {
|
||||||
|
if m.GetSender() == "" {
|
||||||
|
return errNotSetSender
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.sendTime == 0 {
|
||||||
|
return errNotSetSendTime
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = m.CheckBodyArgError(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
508
group/types.go
Normal file
508
group/types.go
Normal file
@ -0,0 +1,508 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Author:1711788888@qq.com
|
||||||
|
* @Date: 2021/5/29 17:43
|
||||||
|
* @Desc: 群组管理
|
||||||
|
*/
|
||||||
|
|
||||||
|
package group
|
||||||
|
|
||||||
|
import "git.echol.cn/loser/tencent-im/internal/types"
|
||||||
|
|
||||||
|
type (
|
||||||
|
// 拉取App中的所有群组(请求)
|
||||||
|
fetchGroupIdsReq struct {
|
||||||
|
Limit int `json:"Limit,omitempty"` // (选填)本次获取的群组 ID 数量的上限,不得超过 10000。如果不填,默认为最大值 10000
|
||||||
|
Next int `json:"Next,omitempty"` // (选填)群太多时分页拉取标志,第一次填0,以后填上一次返回的值,返回的 Next 为0代表拉完了
|
||||||
|
Type string `json:"Type,omitempty"` // (选填)如果仅需要返回特定群组形态的群组,可以通过 Type 进行过滤,但此时返回的 TotalCount 的含义就变成了 App 中属于该群组形态的群组总数。不填为获取所有类型的群组。
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拉取App中的所有群组(响应)
|
||||||
|
fetchGroupIdsResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
Next int `json:"Next"` // 分页拉取的标志
|
||||||
|
TotalCount int `json:"TotalCount"` // App 当前的群组总数。
|
||||||
|
GroupIdList []groupIdItem `json:"GroupIdList"` // 获取到的群组 ID 的集合
|
||||||
|
}
|
||||||
|
|
||||||
|
// FetchGroupIdsRet 拉取App中的所有群组ID返回
|
||||||
|
FetchGroupIdsRet struct {
|
||||||
|
Total int // App 当前的群组总数
|
||||||
|
Next int // 分页拉取的标志
|
||||||
|
HasMore bool // 是否还有更多数据
|
||||||
|
List []string // 群组ID列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// FetchGroupsRet 拉取APP中的所有群返回
|
||||||
|
FetchGroupsRet struct {
|
||||||
|
Total int // App 当前的群组总数
|
||||||
|
Next int // 分页拉取的标志
|
||||||
|
HasMore bool // 是否还有更多数据
|
||||||
|
List []*Group // 群组列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// PullGroupsArg 续拉取群信息(参数)
|
||||||
|
PullGroupsArg struct {
|
||||||
|
Limit int // 分页限制
|
||||||
|
Type Type // 群组类型
|
||||||
|
Filter *Filter // 过滤器
|
||||||
|
}
|
||||||
|
|
||||||
|
// 群ID
|
||||||
|
groupIdItem struct {
|
||||||
|
GroupId string `json:"GroupId"` // 群ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自定义数据
|
||||||
|
customDataItem struct {
|
||||||
|
Key string `json:"Key"`
|
||||||
|
Value interface{} `json:"Value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建群(请求)
|
||||||
|
createGroupReq struct {
|
||||||
|
OwnerUserId string `json:"Owner_Account,omitempty"` // (选填)群主 ID(需是 已导入 的账号)。填写后自动添加到群成员中;如果不填,群没有群主
|
||||||
|
GroupId string `json:"GroupId,omitempty"` // (选填)为了使得群组 ID 更加简单,便于记忆传播,腾讯云支持 App 在通过 REST API 创建群组时 自定义群组 ID
|
||||||
|
Type Type `json:"Type"` // (必填)群组形态,包括 Public(陌生人社交群),Private(即 Work,好友工作群),ChatRoom(即 Meeting,会议群),AVChatRoom(直播群)
|
||||||
|
Name string `json:"Name"` // (必填)群名称,最长30字节,使用 UTF-8 编码,1个汉字占3个字节
|
||||||
|
Introduction string `json:"Introduction,omitempty"` // (选填)群简介,最长240字节,使用 UTF-8 编码,1个汉字占3个字节
|
||||||
|
Notification string `json:"Notification,omitempty"` // (选填)群公告,最长300字节,使用 UTF-8 编码,1个汉字占3个字节
|
||||||
|
FaceUrl string `json:"FaceUrl,omitempty"` // (选填)群头像 URL,最长100字节
|
||||||
|
MaxMemberNum uint `json:"MaxMemberCount,omitempty"` // (选填)最大群成员数量,缺省时的默认值:付费套餐包上限,例如体验版是20,如果升级套餐包,需按照修改群基础资料修改这个字段
|
||||||
|
ApplyJoinOption string `json:"ApplyJoinOption,omitempty"` // (选填)申请加群处理方式。包含 FreeAccess(自由加入),NeedPermission(需要验证),DisableApply(禁止加群),不填默认为 NeedPermission(需要验证) 仅当创建支持申请加群的 群组 时,该字段有效
|
||||||
|
AppDefinedData []*customDataItem `json:"AppDefinedData,omitempty"` // (选填)群组维度的自定义字段,默认情况是没有的,可以通过 即时通信 IM 控制台 进行配置,详情请参阅 自定义字段
|
||||||
|
MemberList []*memberItem `json:"MemberList,omitempty"` // (选填)初始群成员列表,最多100个;成员信息字段详情请参阅 群成员资料
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建群(响应)
|
||||||
|
createGroupResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
GroupId string `json:"GroupId"` // 群ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// 群成员信息
|
||||||
|
memberItem struct {
|
||||||
|
UserId string `json:"Member_Account"` // 群成员ID
|
||||||
|
Role string `json:"Role,omitempty"` // 群内身份
|
||||||
|
JoinTime int64 `json:"JoinTime,omitempty"` // 入群时间
|
||||||
|
MsgSeq int `json:"MsgSeq,omitempty"` // 该成员当前已读消息Seq
|
||||||
|
MsgFlag string `json:"MsgFlag,omitempty"` // 消息接收选项
|
||||||
|
LastSendMsgTime int64 `json:"LastSendMsgTime,omitempty"` // 最后发送消息的时间
|
||||||
|
NameCard string `json:"NameCard,omitempty"` // 群名片
|
||||||
|
ShutUpUntil int64 `json:"ShutUpUntil"` // 禁言截至时间
|
||||||
|
UnreadMsgNum int `json:"UnreadMsgNum,omitempty"` // 待导入群成员的未读消息计数
|
||||||
|
AppMemberDefinedData []*customDataItem `json:"AppMemberDefinedData,omitempty"` // 群成员自定义数据
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解散群(请求)
|
||||||
|
destroyGroupReq struct {
|
||||||
|
GroupId string `json:"GroupId"` // (必填)操作的群 ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// 响应过滤器
|
||||||
|
responseFilter struct {
|
||||||
|
GroupBaseInfoFilter []string `json:"GroupBaseInfoFilter,omitempty"`
|
||||||
|
MemberInfoFilter []string `json:"MemberInfoFilter,omitempty"`
|
||||||
|
GroupCustomDataFilter []string `json:"AppDefinedDataFilter_Group,omitempty"`
|
||||||
|
MemberCustomDataFilter []string `json:"AppDefinedDataFilter_GroupMember,omitempty"`
|
||||||
|
SelfInfoFilter []string `json:"SelfInfoFilter,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取群详细资料(请求)
|
||||||
|
getGroupsReq struct {
|
||||||
|
GroupIds []string `json:"GroupIdList"`
|
||||||
|
ResponseFilter *responseFilter `json:"ResponseFilter,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取群详细资料(响应)
|
||||||
|
getGroupsResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
GroupInfos []*groupInfo `json:"GroupInfo"`
|
||||||
|
}
|
||||||
|
|
||||||
|
groupInfo struct {
|
||||||
|
GroupId string `json:"GroupId"`
|
||||||
|
ErrorCode int `json:"ErrorCode"`
|
||||||
|
ErrorInfo string `json:"ErrorInfo"`
|
||||||
|
Type Type `json:"Type"`
|
||||||
|
Name string `json:"Name"`
|
||||||
|
AppId int `json:"Appid"`
|
||||||
|
Introduction string `json:"Introduction"`
|
||||||
|
Notification string `json:"Notification"`
|
||||||
|
FaceUrl string `json:"FaceUrl"`
|
||||||
|
OwnerUserId string `json:"Owner_Account"`
|
||||||
|
CreateTime int64 `json:"CreateTime"`
|
||||||
|
LastInfoTime int64 `json:"LastInfoTime"`
|
||||||
|
LastMsgTime int64 `json:"LastMsgTime"`
|
||||||
|
NextMsgSeq int `json:"NextMsgSeq"`
|
||||||
|
MemberNum uint `json:"MemberNum"`
|
||||||
|
MaxMemberNum uint `json:"MaxMemberNum"`
|
||||||
|
ApplyJoinOption string `json:"ApplyJoinOption"`
|
||||||
|
ShutUpAllMember string `json:"ShutUpAllMember"`
|
||||||
|
AppDefinedData []customDataItem `json:"AppDefinedData"`
|
||||||
|
MemberList []memberItem `json:"MemberList"`
|
||||||
|
MemberInfo *memberItem `json:"SelfInfo,omitempty"` // 成员在群中的信息(仅在获取用户所加入的群组接口返回)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取群成员详细资料(请求)
|
||||||
|
fetchMembersReq struct {
|
||||||
|
GroupId string `json:"GroupId"` // (必填)需要拉取成员信息的群组的 ID
|
||||||
|
Limit int `json:"Limit"` // (选填)一次最多获取多少个成员的资料,不得超过6000。如果不填,则获取群内全部成员的信息
|
||||||
|
Offset int `json:"Offset"` // (选填)从第几个成员开始获取,如果不填则默认为0,表示从第一个成员开始获取
|
||||||
|
MemberInfoFilter []string `json:"MemberInfoFilter"` // (选填)需要获取哪些信息, 如果没有该字段则为群成员全部资料,成员信息字段详情请参阅 群成员资料
|
||||||
|
MemberRoleFilter []string `json:"MemberRoleFilter"` // (选填)拉取指定身份的群成员资料。如没有填写该字段,默认为所有身份成员资料,成员身份可以为:“Owner”,“Admin”,“Member”
|
||||||
|
MemberCustomDataFilter []string `json:"AppDefinedDataFilter_GroupMember"` // (选填)默认情况是没有的。该字段用来群成员维度的自定义字段过滤器,指定需要获取的群成员维度的自定义字段,群成员维度的自定义字段详情请参阅 自定义字段
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取群成员详细资料(响应)
|
||||||
|
fetchMembersResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
MemberNum int `json:"MemberNum"` // 本群组的群成员总数
|
||||||
|
MemberList []memberItem `json:"MemberList"` // 获取到的群成员列表,其中包含了全部或者指定的群成员信息,成员信息字段详情请参阅 群成员资料
|
||||||
|
}
|
||||||
|
|
||||||
|
// FetchMembersRet 拉取群成员结果
|
||||||
|
FetchMembersRet struct {
|
||||||
|
Total int // 成员数量
|
||||||
|
HasMore bool // 是否还有更多数据
|
||||||
|
List []*Member // 成员列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// PullMembersArg 续拉取群成员(参数)
|
||||||
|
PullMembersArg struct {
|
||||||
|
GroupId string // (必填)需要拉取成员信息的群组的 ID
|
||||||
|
Limit int // (选填)一次最多获取多少个成员的资料,不得超过6000。如果不填,则获取群内全部成员的信息
|
||||||
|
Filter *Filter // (选填)返回过滤器
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改群基础资料(请求)
|
||||||
|
updateGroupReq struct {
|
||||||
|
GroupId string `json:"GroupId"`
|
||||||
|
Name string `json:"Name,omitempty"`
|
||||||
|
Introduction string `json:"Introduction,omitempty"`
|
||||||
|
Notification string `json:"Notification,omitempty"`
|
||||||
|
FaceUrl string `json:"FaceUrl,omitempty"`
|
||||||
|
MaxMemberNum uint `json:"MaxMemberNum,omitempty"`
|
||||||
|
ApplyJoinOption string `json:"ApplyJoinOption,omitempty"`
|
||||||
|
ShutUpAllMember string `json:"ShutUpAllMember,omitempty"`
|
||||||
|
AppDefinedData []customDataItem `json:"AppDefinedData,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加群成员(请求)
|
||||||
|
addMembersReq struct {
|
||||||
|
GroupId string `json:"GroupId"`
|
||||||
|
Silence int `json:"Silence,omitempty"`
|
||||||
|
MemberList []addMemberItem `json:"MemberList"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加群成员(响应)
|
||||||
|
addMembersResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
MemberList []AddMembersResult `json:"MemberList"`
|
||||||
|
}
|
||||||
|
|
||||||
|
addMemberItem struct {
|
||||||
|
UserId string `json:"Member_Account"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddMembersResult 添加群成员结果
|
||||||
|
AddMembersResult struct {
|
||||||
|
UserId string `json:"Member_Account"`
|
||||||
|
Result int `json:"Result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除群成员(请求)
|
||||||
|
deleteMembersReq struct {
|
||||||
|
GroupId string `json:"GroupId"` // (必填)操作的群ID
|
||||||
|
Silence int `json:"Silence"` // (选填)是否静默删人
|
||||||
|
Reason string `json:"Reason"` // (选填)踢出用户原因
|
||||||
|
UserIds []string `json:"MemberToDel_Account"` // (必填)待删除的群成员
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改群成员资料(请求)
|
||||||
|
updateMemberReq struct {
|
||||||
|
GroupId string `json:"GroupId"` // (必填)群ID
|
||||||
|
UserId string `json:"Member_Account"` // (必填)群成员ID
|
||||||
|
Role string `json:"Role,omitempty"` // (选填)群内身份
|
||||||
|
NameCard string `json:"NameCard,omitempty"` // (选填)群名片
|
||||||
|
MsgFlag string `json:"MsgFlag,omitempty"` // (选填)消息接收选项
|
||||||
|
ShutUpUntil *int64 `json:"ShutUpUntil,omitempty"` // (选填)禁言截至时间
|
||||||
|
AppMemberDefinedData []customDataItem `json:"AppMemberDefinedData,omitempty"` // (选填)群成员自定义数据
|
||||||
|
}
|
||||||
|
|
||||||
|
// FetchMemberGroupsArg 拉取用户所加入的群组(参数)
|
||||||
|
FetchMemberGroupsArg struct {
|
||||||
|
UserId string // (必填)用户ID
|
||||||
|
Limit int // (选填)单次拉取的群组数量,如果不填代表所有群组
|
||||||
|
Offset int // (选填)从第多少个群组开始拉取
|
||||||
|
Type Type // (选填)拉取哪种群组类型
|
||||||
|
Filter *Filter // (选填)过滤器
|
||||||
|
IsWithNoActiveGroups bool // (选填)是否获取用户已加入但未激活的 Private(即新版本中 Work,好友工作群) 群信息
|
||||||
|
IsWithLiveRoomGroups bool // (选填)是否获取用户加入的 AVChatRoom(直播群)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FetchMemberGroupsRet 拉取用户所加入的群组(返回)
|
||||||
|
FetchMemberGroupsRet struct {
|
||||||
|
Total int // 群组总数
|
||||||
|
HasMore bool // 是否还有更多数据
|
||||||
|
List []*Group // 列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// PullMemberGroupsArg 续拉取用户所加入的群组(参数)
|
||||||
|
PullMemberGroupsArg struct {
|
||||||
|
UserId string // (必填)用户ID
|
||||||
|
Limit int // (选填)单次拉取的群组数量,如果不填代表所有群组
|
||||||
|
Type Type // (选填)拉取哪种群组类型
|
||||||
|
Filter *Filter // (选填)过滤器
|
||||||
|
IsWithNoActiveGroups bool // (选填)是否获取用户已加入但未激活的 Private(即新版本中 Work,好友工作群) 群信息
|
||||||
|
IsWithLiveRoomGroups bool // (选填)是否获取用户加入的 AVChatRoom(直播群)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拉取用户所加入的群组(请求)
|
||||||
|
fetchMemberGroupsReq struct {
|
||||||
|
UserId string `json:"Member_Account"` // (必填)用户ID
|
||||||
|
Limit int `json:"Limit,omitempty"` // (选填)单次拉取的群组数量,如果不填代表所有群组
|
||||||
|
Offset int `json:"Offset,omitempty"` // (选填)从第多少个群组开始拉取
|
||||||
|
Type Type `json:"Type,omitempty"` // (选填)拉取哪种群组类型
|
||||||
|
WithHugeGroups int `json:"WithHugeGroups,omitempty"`
|
||||||
|
WithNoActiveGroups int `json:"WithNoActiveGroups,omitempty"`
|
||||||
|
ResponseFilter *responseFilter `json:"ResponseFilter,omitempty"` // (选填)响应过滤
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拉取用户所加入的群组(响应)
|
||||||
|
fetchMemberGroupsResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
TotalCount int `json:"TotalCount"`
|
||||||
|
GroupList []groupInfo `json:"GroupIdList"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取
|
||||||
|
getRolesInGroupReq struct {
|
||||||
|
GroupId string `json:"GroupId"`
|
||||||
|
UserIds []string `json:"User_Account"`
|
||||||
|
}
|
||||||
|
|
||||||
|
getRolesInGroupResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
MemberRoleList []memberRole `json:"UserIdList"`
|
||||||
|
}
|
||||||
|
|
||||||
|
memberRole struct {
|
||||||
|
UserId string `json:"Member_Account"`
|
||||||
|
Role string `json:"Role"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量禁言(请求)
|
||||||
|
forbidSendMessageReq struct {
|
||||||
|
GroupId string `json:"GroupId"` // (必填)需要查询的群组 ID
|
||||||
|
UserIds []string `json:"Members_Account"` // (必填)需要禁言的用户帐号,最多支持500个帐号
|
||||||
|
ShutUpTime int64 `json:"ShutUpTime"` // (必填)需禁言时间,单位为秒,为0时表示取消禁言,4294967295为永久禁言。
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取被禁言群成员列表(请求)
|
||||||
|
getShuttedUpMembersReq struct {
|
||||||
|
GroupId string `json:"GroupId"` // (必填)需要获取被禁言成员列表的群组 ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取被禁言群成员列表(响应)
|
||||||
|
getShuttedUpMembersResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
ShuttedUpList []shuttedUp `json:"ShuttedUinList"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 被禁言信息
|
||||||
|
shuttedUp struct {
|
||||||
|
UserId string `json:"Member_Account"` // 用户ID
|
||||||
|
ShuttedUntil int64 `json:"ShuttedUntil"` // 禁言到的时间(使用 UTC 时间,即世界协调时间)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在群组中发送普通消息(请求)
|
||||||
|
sendMessageReq struct {
|
||||||
|
GroupId string `json:"GroupId"` // (必填)向哪个群组发送消息
|
||||||
|
Random uint32 `json:"Random"` // (必填)无符号32位整数
|
||||||
|
MsgPriority string `json:"MsgPriority,omitempty"` // (选填)消息的优先级
|
||||||
|
FromUserId string `json:"From_Account,omitempty"` // (选填)消息来源帐号
|
||||||
|
MsgBody []*types.MsgBody `json:"MsgBody"` // (必填)消息体
|
||||||
|
OnlineOnlyFlag int `json:"MsgOnlineOnlyFlag,omitempty"` // (选填)1表示消息仅发送在线成员,默认0表示发送所有成员,AVChatRoom(直播群)不支持该参数
|
||||||
|
SendMsgControl []string `json:"SendMsgControl,omitempty"` // (选填)消息发送权限,NoLastMsg 只对单条消息有效,表示不更新最近联系人会话;NoUnread 不计未读,只对单条消息有效。(如果该消息 MsgOnlineOnlyFlag 设置为1,则不允许使用该字段。)
|
||||||
|
ForbidCallbackControl []string `json:"ForbidCallbackControl,omitempty"` // (选填)消息回调禁止开关,只对单条消息有效
|
||||||
|
OfflinePushInfo *types.OfflinePushInfo `json:"OfflinePushInfo,omitempty"` // (选填)离线推送信息配置
|
||||||
|
CloudCustomData string `json:"CloudCustomData,omitempty"` // (选填)消息自定义数据(云端保存,会发送到对端,程序卸载重装后还能拉取到)
|
||||||
|
GroupAtInfo []atInfo `json:"GroupAtInfo,omitempty"` // (选填)@某个用户或者所有人
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在群组中发送普通消息(响应)
|
||||||
|
sendMessageResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
MsgTime int `json:"MsgTime"`
|
||||||
|
MsgSeq int `json:"MsgSeq"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendMessageRet 发送消息结果
|
||||||
|
SendMessageRet struct {
|
||||||
|
MsgSeq int // 消息唯一标识,用于撤回。长度不超过50个字符
|
||||||
|
MsgTime int // 消息时间戳,UNIX 时间戳
|
||||||
|
}
|
||||||
|
|
||||||
|
atInfo struct {
|
||||||
|
GroupAtAllFlag int `json:"GroupAtAllFlag"`
|
||||||
|
GroupAtUserId string `json:"GroupAt_Account,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在群组中发送系统通知(请求)
|
||||||
|
sendNotificationReq struct {
|
||||||
|
GroupId string `json:"GroupId"` // (必填)向哪个群组发送系统通知
|
||||||
|
Content string `json:"Content"` // (必填)系统通知的内容
|
||||||
|
UserIds []string `json:"ToMembers_Account,omitempty"` // (选填)接收者群成员列表,请填写接收者 UserID,不填或为空表示全员下发
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转让群主(请求)
|
||||||
|
changeGroupOwnerReq struct {
|
||||||
|
GroupId string `json:"GroupId"`
|
||||||
|
OwnerUserId string `json:"NewOwner_Account"`
|
||||||
|
}
|
||||||
|
|
||||||
|
msgSeqItem struct {
|
||||||
|
MsgSeq int `json:"MsgSeq"` // 请求撤回的消息seq
|
||||||
|
}
|
||||||
|
|
||||||
|
// 撤销消息(请求)
|
||||||
|
revokeMessagesReq struct {
|
||||||
|
GroupId string `json:"GroupId"` // (必填)操作的群ID
|
||||||
|
MsgSeqList []msgSeqItem `json:"MsgSeqList"` // (必填)被撤回的消息 seq 列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// 撤销消息(响应)
|
||||||
|
revokeMessagesResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
Results []revokeMessageResult `json:"Results"` // 撤销结果列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// 撤销消息结果
|
||||||
|
revokeMessageResult struct {
|
||||||
|
MsgSeq int `json:"MsgSeq"` // 单个被撤回消息的 seq
|
||||||
|
RetCode int `json:"RetCode"` // 单个消息的被撤回结果:0表示成功;其它表示失败
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导入群基础资料(请求)
|
||||||
|
importGroupReq struct {
|
||||||
|
OwnerUserId string `json:"Owner_Account,omitempty"` // (选填)群主 ID(需是 已导入 的账号)。填写后自动添加到群成员中;如果不填,群没有群主
|
||||||
|
GroupId string `json:"GroupId,omitempty"` // (选填)为了使得群组 ID 更加简单,便于记忆传播,腾讯云支持 App 在通过 REST API 创建群组时 自定义群组 ID
|
||||||
|
Type Type `json:"Type"` // (必填)群组形态,包括 Public(陌生人社交群),Private(即 Work,好友工作群),ChatRoom(即 Meeting,会议群),AVChatRoom(直播群)
|
||||||
|
Name string `json:"Name"` // (必填)群名称,最长30字节,使用 UTF-8 编码,1个汉字占3个字节
|
||||||
|
Introduction string `json:"Introduction,omitempty"` // (选填)群简介,最长240字节,使用 UTF-8 编码,1个汉字占3个字节
|
||||||
|
Notification string `json:"Notification,omitempty"` // (选填)群公告,最长300字节,使用 UTF-8 编码,1个汉字占3个字节
|
||||||
|
FaceUrl string `json:"FaceUrl,omitempty"` // (选填)群头像 URL,最长100字节
|
||||||
|
MaxMemberNum uint `json:"MaxMemberCount,omitempty"` // (选填)最大群成员数量,缺省时的默认值:付费套餐包上限,例如体验版是20,如果升级套餐包,需按照修改群基础资料修改这个字段
|
||||||
|
ApplyJoinOption string `json:"ApplyJoinOption,omitempty"` // (选填)申请加群处理方式。包含 FreeAccess(自由加入),NeedPermission(需要验证),DisableApply(禁止加群),不填默认为 NeedPermission(需要验证) 仅当创建支持申请加群的 群组 时,该字段有效
|
||||||
|
AppDefinedData []*customDataItem `json:"AppDefinedData,omitempty"` // (选填)群组维度的自定义字段,默认情况是没有的,可以通过 即时通信 IM 控制台 进行配置,详情请参阅 自定义字段
|
||||||
|
CreateTime int64 `json:"CreateTime"` // (选填)群组的创建时间
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导入群基础资料(响应)
|
||||||
|
importGroupResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
GroupId string `json:"GroupId"` // 群ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// 消息信息
|
||||||
|
messageItem struct {
|
||||||
|
FromUserId string `json:"From_Account"` // (必填)消息来源帐号
|
||||||
|
MsgBody []*types.MsgBody `json:"MsgBody"` // (必填)消息体
|
||||||
|
SendTime int64 `json:"SendTime"` // (必填)消息发送时间
|
||||||
|
Random uint32 `json:"Random,omitempty"` // (选填)无符号32位整数
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导入群消息(请求)
|
||||||
|
importMessagesReq struct {
|
||||||
|
GroupId string `json:"GroupId"` // (必填)要导入消息的群ID
|
||||||
|
Messages []messageItem `json:"MsgList"` // (必填)导入的消息列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导入群消息(响应)
|
||||||
|
importMessagesResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
Results []ImportMessagesResult `json:"ImportMsgResult"` // 导入群消息结果
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImportMessagesResult 导入群消息结果
|
||||||
|
ImportMessagesResult struct {
|
||||||
|
MsgSeq int `json:"MsgSeq"` // 消息序列号,唯一标示一条消息
|
||||||
|
MsgTime int `json:"MsgTime"` // 消息的时间戳
|
||||||
|
Result int `json:"Result"` // 单条消息导入结果 0表示单条消息成功 10004表示单条消息发送时间无效 80001表示单条消息包含脏字,拒绝存储此消息 80002表示为消息内容过长,目前支持8000字节的消息,请调整消息长度
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导入群成员(请求)
|
||||||
|
importMembersReq struct {
|
||||||
|
GroupId string `json:"GroupId"` // (必填)操作的群ID
|
||||||
|
Members []*memberItem `json:"MemberList"` // (必填)添加的群成员数组
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导入群成员(响应)
|
||||||
|
importMembersResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
Results []ImportMemberResult `json:"MemberList"` // 添加的群成员结果
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImportMemberResult 导入成员结果
|
||||||
|
ImportMemberResult struct {
|
||||||
|
UserId string `json:"Member_Account"` // 群成员帐号
|
||||||
|
Result int `json:"Result"` // 导入结果:0表示失败;1表示成功;2表示已经是群成员
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置成员未读消息计数(请求)
|
||||||
|
setMemberUnreadMsgNumReq struct {
|
||||||
|
GroupId string `json:"GroupId"` // (必填)操作的群 ID
|
||||||
|
UserId string `json:"Member_Account"` // (必填)要操作的群成员
|
||||||
|
UnreadMsgNum int `json:"UnreadMsgNum"` // (必填)成员未读消息数
|
||||||
|
}
|
||||||
|
|
||||||
|
// 撤回指定用户发送的消息(请求)
|
||||||
|
revokeMemberMessagesReq struct {
|
||||||
|
GroupId string `json:"GroupId"` // (必填)要撤回消息的群 ID
|
||||||
|
UserId string `json:"Sender_Account"` // (必填)被撤回消息的发送者 ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拉取群历史消息(请求)
|
||||||
|
fetchMessagesReq struct {
|
||||||
|
GroupId string `json:"GroupId"` // (必填)要拉取历史消息的群组 ID
|
||||||
|
ReqMsgSeq int `json:"ReqMsgSeq"` // (选填)拉取消息的最大seq
|
||||||
|
ReqMsgNumber int `json:"ReqMsgNumber,omitempty"` // (必填)拉取的历史消息的条数,目前一次请求最多返回20条历史消息,所以这里最好小于等于20
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拉取群历史消息(响应)
|
||||||
|
fetchMessagesResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
GroupId string `json:"GroupId"`
|
||||||
|
IsFinished int `json:"IsFinished"`
|
||||||
|
RspMsgList []rspMsgItem `json:"RspMsgList"`
|
||||||
|
}
|
||||||
|
|
||||||
|
FetchMessagesRet struct {
|
||||||
|
IsFinished int // 是否返回了请求区间的全部消息 当成功返回了请求区间的全部消息时,值为1; 当消息长度太长或者区间太大(超过20)导致无法返回全部消息时,值为0; 当消息长度太长或者区间太大(超过20)且所有消息都过期时,值为2
|
||||||
|
HasMore bool // 是否还有更多数据
|
||||||
|
NextSeq int // 下一个消息Seq
|
||||||
|
List []*Message // 列表
|
||||||
|
}
|
||||||
|
|
||||||
|
rspMsgItem struct {
|
||||||
|
FromUserId string `json:"From_Account"`
|
||||||
|
IsPlaceMsg int `json:"IsPlaceMsg"`
|
||||||
|
MsgBody []types.MsgBody `json:"MsgBody"`
|
||||||
|
MsgPriority int `json:"MsgPriority"`
|
||||||
|
MsgRandom uint32 `json:"MsgRandom"`
|
||||||
|
MsgSeq int `json:"MsgSeq"`
|
||||||
|
MsgTimeStamp int64 `json:"MsgTimeStamp"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取直播群在线人数(请求)
|
||||||
|
getOnlineMemberNumReq struct {
|
||||||
|
GroupId string `json:"GroupId"` // (必填)操作的群ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取直播群在线人数(响应)
|
||||||
|
getOnlineMemberNumResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
OnlineMemberNum int `json:"OnlineMemberNum"` // 该群组的在线人数
|
||||||
|
}
|
||||||
|
)
|
212
im.go
Normal file
212
im.go
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Author: 1711788888@qq.com
|
||||||
|
* @Date: 2022/9/26 20:29
|
||||||
|
* @Desc: 腾讯云IM
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.echol.cn/loser/tencent-im/account"
|
||||||
|
"git.echol.cn/loser/tencent-im/callback"
|
||||||
|
"git.echol.cn/loser/tencent-im/group"
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/core"
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/sign"
|
||||||
|
"git.echol.cn/loser/tencent-im/mute"
|
||||||
|
"git.echol.cn/loser/tencent-im/operation"
|
||||||
|
"git.echol.cn/loser/tencent-im/private"
|
||||||
|
"git.echol.cn/loser/tencent-im/profile"
|
||||||
|
"git.echol.cn/loser/tencent-im/push"
|
||||||
|
"git.echol.cn/loser/tencent-im/recentcontact"
|
||||||
|
"git.echol.cn/loser/tencent-im/sns"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Error = core.Error
|
||||||
|
|
||||||
|
type (
|
||||||
|
IM interface {
|
||||||
|
// GetUserSig 获取UserSig签名
|
||||||
|
GetUserSig(userId string, expiration ...int) UserSig
|
||||||
|
// SNS 获取关系链管理接口
|
||||||
|
SNS() sns.API
|
||||||
|
// Mute 获取全局禁言管理接口
|
||||||
|
Mute() mute.API
|
||||||
|
// Push 获取全员推送接口
|
||||||
|
Push() push.API
|
||||||
|
// Group 获取群组管理接口
|
||||||
|
Group() group.API
|
||||||
|
// Account 获取账号管理接口
|
||||||
|
Account() account.API
|
||||||
|
// Profile 获取资料管理接口
|
||||||
|
Profile() profile.API
|
||||||
|
// Private 获取私聊消息接口
|
||||||
|
Private() private.API
|
||||||
|
// Operation 获取运营管理接口
|
||||||
|
Operation() operation.API
|
||||||
|
// RecentContact 获取最近联系人接口
|
||||||
|
RecentContact() recentcontact.API
|
||||||
|
// Callback 获取回调接口
|
||||||
|
Callback() callback.Callback
|
||||||
|
}
|
||||||
|
|
||||||
|
Options struct {
|
||||||
|
AppId int // 应用SDKAppID,可在即时通信 IM 控制台 的应用卡片中获取。
|
||||||
|
AppSecret string // 密钥信息,可在即时通信 IM 控制台 的应用详情页面中获取,具体操作请参见 获取密钥
|
||||||
|
UserId string // 用户ID
|
||||||
|
Expiration int // UserSig过期时间
|
||||||
|
}
|
||||||
|
|
||||||
|
UserSig struct {
|
||||||
|
UserSig string // 用户签名
|
||||||
|
ExpireAt int64 // 签名过期时间
|
||||||
|
}
|
||||||
|
|
||||||
|
im struct {
|
||||||
|
opt *Options
|
||||||
|
client core.Client
|
||||||
|
sns struct {
|
||||||
|
once sync.Once
|
||||||
|
instance sns.API
|
||||||
|
}
|
||||||
|
mute struct {
|
||||||
|
once sync.Once
|
||||||
|
instance mute.API
|
||||||
|
}
|
||||||
|
push struct {
|
||||||
|
once sync.Once
|
||||||
|
instance push.API
|
||||||
|
}
|
||||||
|
group struct {
|
||||||
|
once sync.Once
|
||||||
|
instance group.API
|
||||||
|
}
|
||||||
|
account struct {
|
||||||
|
once sync.Once
|
||||||
|
instance account.API
|
||||||
|
}
|
||||||
|
profile struct {
|
||||||
|
once sync.Once
|
||||||
|
instance profile.API
|
||||||
|
}
|
||||||
|
private struct {
|
||||||
|
once sync.Once
|
||||||
|
instance private.API
|
||||||
|
}
|
||||||
|
operation struct {
|
||||||
|
once sync.Once
|
||||||
|
instance operation.API
|
||||||
|
}
|
||||||
|
recentcontact struct {
|
||||||
|
once sync.Once
|
||||||
|
instance recentcontact.API
|
||||||
|
}
|
||||||
|
callback struct {
|
||||||
|
once sync.Once
|
||||||
|
instance callback.Callback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewIM(opt *Options) IM {
|
||||||
|
return &im{opt: opt, client: core.NewClient(&core.Options{
|
||||||
|
AppId: opt.AppId,
|
||||||
|
AppSecret: opt.AppSecret,
|
||||||
|
UserId: opt.UserId,
|
||||||
|
Expiration: opt.Expiration,
|
||||||
|
})}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserSig 获取UserSig签名
|
||||||
|
func (i *im) GetUserSig(userId string, expiration ...int) UserSig {
|
||||||
|
if len(expiration) == 0 {
|
||||||
|
expiration = append(expiration, i.opt.Expiration)
|
||||||
|
}
|
||||||
|
|
||||||
|
userSig, _ := sign.GenUserSig(i.opt.AppId, i.opt.AppSecret, userId, expiration[0])
|
||||||
|
expireAt := time.Now().Add(time.Duration(i.opt.Expiration) * time.Second).Unix()
|
||||||
|
return UserSig{UserSig: userSig, ExpireAt: expireAt}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SNS 获取关系链管理接口ok
|
||||||
|
func (i *im) SNS() sns.API {
|
||||||
|
i.sns.once.Do(func() {
|
||||||
|
i.sns.instance = sns.NewAPI(i.client)
|
||||||
|
})
|
||||||
|
return i.sns.instance
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mute 获取全局禁言管理接口ok
|
||||||
|
func (i *im) Mute() mute.API {
|
||||||
|
i.mute.once.Do(func() {
|
||||||
|
i.mute.instance = mute.NewAPI(i.client)
|
||||||
|
})
|
||||||
|
return i.mute.instance
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push 获取全员推送接口
|
||||||
|
func (i *im) Push() push.API {
|
||||||
|
i.push.once.Do(func() {
|
||||||
|
i.push.instance = push.NewAPI(i.client)
|
||||||
|
})
|
||||||
|
return i.push.instance
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group 获取群组管理接口
|
||||||
|
func (i *im) Group() group.API {
|
||||||
|
i.group.once.Do(func() {
|
||||||
|
i.group.instance = group.NewAPI(i.client)
|
||||||
|
})
|
||||||
|
return i.group.instance
|
||||||
|
}
|
||||||
|
|
||||||
|
// Account 获取账号管理接口ok
|
||||||
|
func (i *im) Account() account.API {
|
||||||
|
i.account.once.Do(func() {
|
||||||
|
i.account.instance = account.NewAPI(i.client)
|
||||||
|
})
|
||||||
|
return i.account.instance
|
||||||
|
}
|
||||||
|
|
||||||
|
// Profile 获取资料管理接口ok
|
||||||
|
func (i *im) Profile() profile.API {
|
||||||
|
i.profile.once.Do(func() {
|
||||||
|
i.profile.instance = profile.NewAPI(i.client)
|
||||||
|
})
|
||||||
|
return i.profile.instance
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private 获取私聊消息接口ok
|
||||||
|
func (i *im) Private() private.API {
|
||||||
|
i.private.once.Do(func() {
|
||||||
|
i.private.instance = private.NewAPI(i.client)
|
||||||
|
})
|
||||||
|
return i.private.instance
|
||||||
|
}
|
||||||
|
|
||||||
|
// Operation 获取运营管理接口ok
|
||||||
|
func (i *im) Operation() operation.API {
|
||||||
|
i.operation.once.Do(func() {
|
||||||
|
i.operation.instance = operation.NewAPI(i.client)
|
||||||
|
})
|
||||||
|
return i.operation.instance
|
||||||
|
}
|
||||||
|
|
||||||
|
// RecentContact 获取最近联系人接口ok
|
||||||
|
func (i *im) RecentContact() recentcontact.API {
|
||||||
|
i.recentcontact.once.Do(func() {
|
||||||
|
i.recentcontact.instance = recentcontact.NewAPI(i.client)
|
||||||
|
})
|
||||||
|
return i.recentcontact.instance
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callback 获取回调接口
|
||||||
|
func (i *im) Callback() callback.Callback {
|
||||||
|
i.callback.once.Do(func() {
|
||||||
|
i.callback.instance = callback.NewCallback(i.opt.AppId)
|
||||||
|
})
|
||||||
|
return i.callback.instance
|
||||||
|
}
|
1511
im_test.go
Normal file
1511
im_test.go
Normal file
File diff suppressed because it is too large
Load Diff
108
internal/conv/conv.go
Normal file
108
internal/conv/conv.go
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Email:1711788888@qq.com
|
||||||
|
* @Date: 2021/8/31 11:16 下午
|
||||||
|
* @Desc: TODO
|
||||||
|
*/
|
||||||
|
|
||||||
|
package conv
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type stringInterface interface {
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type errorInterface interface {
|
||||||
|
Error() string
|
||||||
|
}
|
||||||
|
|
||||||
|
func String(any interface{}) string {
|
||||||
|
switch v := any.(type) {
|
||||||
|
case nil:
|
||||||
|
return ""
|
||||||
|
case string:
|
||||||
|
return v
|
||||||
|
case int:
|
||||||
|
return strconv.Itoa(v)
|
||||||
|
case int8:
|
||||||
|
return strconv.Itoa(int(v))
|
||||||
|
case int16:
|
||||||
|
return strconv.Itoa(int(v))
|
||||||
|
case int32:
|
||||||
|
return strconv.Itoa(int(v))
|
||||||
|
case int64:
|
||||||
|
return strconv.FormatInt(v, 10)
|
||||||
|
case uint:
|
||||||
|
return strconv.FormatUint(uint64(v), 10)
|
||||||
|
case uint8:
|
||||||
|
return strconv.FormatUint(uint64(v), 10)
|
||||||
|
case uint16:
|
||||||
|
return strconv.FormatUint(uint64(v), 10)
|
||||||
|
case uint64:
|
||||||
|
return strconv.FormatUint(v, 10)
|
||||||
|
case float32:
|
||||||
|
return strconv.FormatFloat(float64(v), 'f', -1, 32)
|
||||||
|
case float64:
|
||||||
|
return strconv.FormatFloat(v, 'f', -1, 64)
|
||||||
|
case bool:
|
||||||
|
return strconv.FormatBool(v)
|
||||||
|
case []byte:
|
||||||
|
return string(v)
|
||||||
|
case time.Time:
|
||||||
|
return v.String()
|
||||||
|
case *time.Time:
|
||||||
|
if v == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return v.String()
|
||||||
|
default:
|
||||||
|
if v == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if i, ok := v.(stringInterface); ok {
|
||||||
|
return i.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
if i, ok := v.(errorInterface); ok {
|
||||||
|
return i.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
rv = reflect.ValueOf(v)
|
||||||
|
kind = rv.Kind()
|
||||||
|
)
|
||||||
|
|
||||||
|
switch kind {
|
||||||
|
case reflect.Chan,
|
||||||
|
reflect.Map,
|
||||||
|
reflect.Slice,
|
||||||
|
reflect.Func,
|
||||||
|
reflect.Ptr,
|
||||||
|
reflect.Interface,
|
||||||
|
reflect.UnsafePointer:
|
||||||
|
if rv.IsNil() {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
case reflect.String:
|
||||||
|
return rv.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
if kind == reflect.Ptr {
|
||||||
|
return String(rv.Elem().Interface())
|
||||||
|
}
|
||||||
|
|
||||||
|
if b, e := json.Marshal(v); e != nil {
|
||||||
|
return fmt.Sprint(v)
|
||||||
|
} else {
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
146
internal/core/client.go
Normal file
146
internal/core/client.go
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Email:1711788888@qq.com
|
||||||
|
* @Date: 2021/8/27 11:31 上午
|
||||||
|
* @Desc: TODO
|
||||||
|
*/
|
||||||
|
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.echol.cn/loser/http"
|
||||||
|
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/enum"
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/sign"
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultBaseUrl = "https://console.tim.qq.com"
|
||||||
|
defaultVersion = "v4"
|
||||||
|
defaultContentType = "json"
|
||||||
|
defaultExpiration = 3600
|
||||||
|
)
|
||||||
|
|
||||||
|
var invalidResponse = NewError(enum.InvalidResponseCode, "invalid response")
|
||||||
|
|
||||||
|
type Client interface {
|
||||||
|
// Get GET请求
|
||||||
|
Get(serviceName string, command string, data interface{}, resp interface{}) error
|
||||||
|
// Post POST请求
|
||||||
|
Post(serviceName string, command string, data interface{}, resp interface{}) error
|
||||||
|
// Put PUT请求
|
||||||
|
Put(serviceName string, command string, data interface{}, resp interface{}) error
|
||||||
|
// Patch PATCH请求
|
||||||
|
Patch(serviceName string, command string, data interface{}, resp interface{}) error
|
||||||
|
// Delete DELETE请求
|
||||||
|
Delete(serviceName string, command string, data interface{}, resp interface{}) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type client struct {
|
||||||
|
client *http.Client
|
||||||
|
opt *Options
|
||||||
|
userSig string
|
||||||
|
userSigExpireAt int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type Options struct {
|
||||||
|
AppId int // 应用SDKAppID,可在即时通信 IM 控制台 的应用卡片中获取。
|
||||||
|
AppSecret string // 密钥信息,可在即时通信 IM 控制台 的应用详情页面中获取,具体操作请参见 获取密钥
|
||||||
|
UserId string // 用户ID
|
||||||
|
Expiration int // UserSig过期时间
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient(opt *Options) Client {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
c := new(client)
|
||||||
|
c.opt = opt
|
||||||
|
c.client = http.NewClient()
|
||||||
|
c.client.SetContentType(http.ContentTypeJson)
|
||||||
|
c.client.SetBaseUrl(defaultBaseUrl)
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get GET请求
|
||||||
|
func (c *client) Get(serviceName string, command string, data interface{}, resp interface{}) error {
|
||||||
|
return c.request(http.MethodGet, serviceName, command, data, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post POST请求
|
||||||
|
func (c *client) Post(serviceName string, command string, data interface{}, resp interface{}) error {
|
||||||
|
return c.request(http.MethodPost, serviceName, command, data, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put PUT请求
|
||||||
|
func (c *client) Put(serviceName string, command string, data interface{}, resp interface{}) error {
|
||||||
|
return c.request(http.MethodPut, serviceName, command, data, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch PATCH请求
|
||||||
|
func (c *client) Patch(serviceName string, command string, data interface{}, resp interface{}) error {
|
||||||
|
return c.request(http.MethodPatch, serviceName, command, data, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete DELETE请求
|
||||||
|
func (c *client) Delete(serviceName string, command string, data interface{}, resp interface{}) error {
|
||||||
|
return c.request(http.MethodDelete, serviceName, command, data, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// request Request请求
|
||||||
|
func (c *client) request(method, serviceName, command string, data, resp interface{}) error {
|
||||||
|
res, err := c.client.Request(method, c.buildUrl(serviceName, command), data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = res.Scan(resp); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if r, ok := resp.(types.ActionBaseRespInterface); ok {
|
||||||
|
if r.GetActionStatus() == enum.FailActionStatus {
|
||||||
|
return NewError(r.GetErrorCode(), r.GetErrorInfo())
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.GetErrorCode() != enum.SuccessCode {
|
||||||
|
return NewError(r.GetErrorCode(), r.GetErrorInfo())
|
||||||
|
}
|
||||||
|
} else if r, ok := resp.(types.BaseRespInterface); ok {
|
||||||
|
if r.GetErrorCode() != enum.SuccessCode {
|
||||||
|
return NewError(r.GetErrorCode(), r.GetErrorInfo())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return invalidResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildUrl 构建一个请求URL
|
||||||
|
func (c *client) buildUrl(serviceName string, command string) string {
|
||||||
|
format := "/%s/%s/%s?sdkappid=%d&identifier=%s&usersig=%s&random=%d&contenttype=%s"
|
||||||
|
random := rand.Int31()
|
||||||
|
userSig := c.getUserSig()
|
||||||
|
return fmt.Sprintf(format, defaultVersion, serviceName, command, c.opt.AppId, c.opt.UserId, userSig, random, defaultContentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getUserSig 获取签名
|
||||||
|
func (c *client) getUserSig() string {
|
||||||
|
now, expiration := time.Now(), c.opt.Expiration
|
||||||
|
|
||||||
|
if expiration <= 0 {
|
||||||
|
expiration = defaultExpiration
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.userSig == "" || c.userSigExpireAt <= now.Unix() {
|
||||||
|
c.userSig, _ = sign.GenUserSig(c.opt.AppId, c.opt.AppSecret, c.opt.UserId, expiration)
|
||||||
|
c.userSigExpireAt = now.Add(time.Duration(expiration) * time.Second).Unix()
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.userSig
|
||||||
|
}
|
38
internal/core/error.go
Normal file
38
internal/core/error.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Email:1711788888@qq.com
|
||||||
|
* @Date: 2021/8/27 1:12 下午
|
||||||
|
* @Desc: TODO
|
||||||
|
*/
|
||||||
|
|
||||||
|
package core
|
||||||
|
|
||||||
|
type Error interface {
|
||||||
|
error
|
||||||
|
Code() int
|
||||||
|
Message() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type respError struct {
|
||||||
|
code int
|
||||||
|
message string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewError(code int, message string) Error {
|
||||||
|
return &respError{
|
||||||
|
code: code,
|
||||||
|
message: message,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *respError) Error() string {
|
||||||
|
return e.message
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *respError) Code() int {
|
||||||
|
return e.code
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *respError) Message() string {
|
||||||
|
return e.message
|
||||||
|
}
|
170
internal/entity/mesage.go
Normal file
170
internal/entity/mesage.go
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Author:1711788888@qq.com
|
||||||
|
* @Date: 2021/9/3 18:19
|
||||||
|
* @Desc: 基础消息实体
|
||||||
|
*/
|
||||||
|
|
||||||
|
package entity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"math/rand"
|
||||||
|
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/enum"
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errInvalidMsgContent = errors.New("invalid message content")
|
||||||
|
errInvalidMsgLifeTime = errors.New("invalid message life time")
|
||||||
|
errNotSetMsgContent = errors.New("message content is not set")
|
||||||
|
)
|
||||||
|
|
||||||
|
type Message struct {
|
||||||
|
sender string // 发送方UserId
|
||||||
|
lifeTime int // 消息离线保存时长(单位:秒),最长为7天(604800秒)
|
||||||
|
random uint32 // 消息随机数,由随机函数产生
|
||||||
|
body []*types.MsgBody // 消息体
|
||||||
|
offlinePush *offlinePush // 推送实体
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSender 设置发送方UserId
|
||||||
|
func (m *Message) SetSender(userId string) {
|
||||||
|
m.sender = userId
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSender 获取发送者
|
||||||
|
func (m *Message) GetSender() string {
|
||||||
|
return m.sender
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLifeTime 设置消息离线保存时长
|
||||||
|
func (m *Message) SetLifeTime(lifeTime int) {
|
||||||
|
m.lifeTime = lifeTime
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLifeTime 获取消息离线保存时长
|
||||||
|
func (m *Message) GetLifeTime() int {
|
||||||
|
return m.lifeTime
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRandom 设置消息随机数
|
||||||
|
func (m *Message) SetRandom(random uint32) {
|
||||||
|
m.random = random
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRandom 获取消息随机数
|
||||||
|
func (m *Message) GetRandom() uint32 {
|
||||||
|
if m.random == 0 {
|
||||||
|
m.random = rand.Uint32()
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.random
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddContent 添加消息内容(添加会累加之前的消息内容)
|
||||||
|
func (m *Message) AddContent(msgContent ...interface{}) {
|
||||||
|
if m.body == nil {
|
||||||
|
m.body = make([]*types.MsgBody, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(msgContent) > 0 {
|
||||||
|
var msgType string
|
||||||
|
for _, content := range msgContent {
|
||||||
|
switch content.(type) {
|
||||||
|
case types.MsgTextContent, *types.MsgTextContent:
|
||||||
|
msgType = enum.MsgText
|
||||||
|
case types.MsgLocationContent, *types.MsgLocationContent:
|
||||||
|
msgType = enum.MsgLocation
|
||||||
|
case types.MsgFaceContent, *types.MsgFaceContent:
|
||||||
|
msgType = enum.MsgFace
|
||||||
|
case types.MsgCustomContent, *types.MsgCustomContent:
|
||||||
|
msgType = enum.MsgCustom
|
||||||
|
case types.MsgSoundContent, *types.MsgSoundContent:
|
||||||
|
msgType = enum.MsgSound
|
||||||
|
case types.MsgImageContent, *types.MsgImageContent:
|
||||||
|
msgType = enum.MsgImage
|
||||||
|
case types.MsgFileContent, *types.MsgFileContent:
|
||||||
|
msgType = enum.MsgFile
|
||||||
|
case types.MsgVideoContent, *types.MsgVideoContent:
|
||||||
|
msgType = enum.MsgVideo
|
||||||
|
default:
|
||||||
|
msgType = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
m.body = append(m.body, &types.MsgBody{
|
||||||
|
MsgType: msgType,
|
||||||
|
MsgContent: content,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetContent 设置消息内容(设置会冲掉之前的消息内容)
|
||||||
|
func (m *Message) SetContent(msgContent ...interface{}) {
|
||||||
|
if m.body != nil {
|
||||||
|
m.body = m.body[0:0]
|
||||||
|
}
|
||||||
|
m.AddContent(msgContent...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBody 获取消息体
|
||||||
|
func (m *Message) GetBody() []*types.MsgBody {
|
||||||
|
return m.body
|
||||||
|
}
|
||||||
|
|
||||||
|
// OfflinePush 新建离线推送对象
|
||||||
|
func (m *Message) OfflinePush() *offlinePush {
|
||||||
|
if m.offlinePush == nil {
|
||||||
|
m.offlinePush = newOfflinePush()
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.offlinePush
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOfflinePushInfo 获取离线推送消息
|
||||||
|
func (m *Message) GetOfflinePushInfo() *types.OfflinePushInfo {
|
||||||
|
if m.offlinePush == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &types.OfflinePushInfo{
|
||||||
|
PushFlag: m.offlinePush.pushFlag,
|
||||||
|
Title: m.offlinePush.title,
|
||||||
|
Desc: m.offlinePush.desc,
|
||||||
|
Ext: m.offlinePush.ext,
|
||||||
|
AndroidInfo: m.offlinePush.androidInfo,
|
||||||
|
ApnsInfo: m.offlinePush.apnsInfo,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckLifeTimeArgError 检测参数错误
|
||||||
|
func (m *Message) CheckLifeTimeArgError() error {
|
||||||
|
if m.body != nil && len(m.body) > 0 {
|
||||||
|
for _, item := range m.body {
|
||||||
|
if item.MsgType == "" {
|
||||||
|
return errInvalidMsgContent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return errNotSetMsgContent
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckBodyArgError 检测参数错误
|
||||||
|
func (m *Message) CheckBodyArgError() error {
|
||||||
|
if m.body != nil && len(m.body) > 0 {
|
||||||
|
for _, item := range m.body {
|
||||||
|
if item.MsgType == "" {
|
||||||
|
return errInvalidMsgContent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return errNotSetMsgContent
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
150
internal/entity/offline_push.go
Normal file
150
internal/entity/offline_push.go
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Author:1711788888@qq.com
|
||||||
|
* @Date: 2021/9/3 18:47
|
||||||
|
* @Desc: 离线推送
|
||||||
|
*/
|
||||||
|
|
||||||
|
package entity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/conv"
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type offlinePush struct {
|
||||||
|
pushFlag int // 推送标识。0表示推送,1表示不离线推送。
|
||||||
|
title string // 离线推送标题。该字段为 iOS 和 Android 共用。
|
||||||
|
desc string // 离线推送内容。
|
||||||
|
ext string // 离线推送透传内容。
|
||||||
|
androidInfo *types.AndroidInfo // Android离线推送消息
|
||||||
|
apnsInfo *types.ApnsInfo // IOS离线推送消息
|
||||||
|
}
|
||||||
|
|
||||||
|
func newOfflinePush() *offlinePush {
|
||||||
|
return &offlinePush{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPushFlag 设置推送消息
|
||||||
|
func (o *offlinePush) SetPushFlag(pushFlag types.PushFlag) {
|
||||||
|
o.pushFlag = int(pushFlag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTitle 设置离线推送标题
|
||||||
|
func (o *offlinePush) SetTitle(title string) {
|
||||||
|
o.title = title
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDesc 设置离线推送内容
|
||||||
|
func (o *offlinePush) SetDesc(desc string) {
|
||||||
|
o.desc = desc
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetExt 设置离线推送透传内容
|
||||||
|
func (o *offlinePush) SetExt(ext interface{}) {
|
||||||
|
o.ext = conv.String(ext)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAndroidSound 设置Android离线推送声音文件路径
|
||||||
|
func (o *offlinePush) SetAndroidSound(sound string) {
|
||||||
|
if o.androidInfo == nil {
|
||||||
|
o.androidInfo = &types.AndroidInfo{}
|
||||||
|
}
|
||||||
|
o.androidInfo.Sound = sound
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAndroidHuaWeiChannelId 设置华为手机 EMUI 10.0 及以上的通知渠道字段
|
||||||
|
func (o *offlinePush) SetAndroidHuaWeiChannelId(channelId string) {
|
||||||
|
if o.androidInfo == nil {
|
||||||
|
o.androidInfo = &types.AndroidInfo{}
|
||||||
|
}
|
||||||
|
o.androidInfo.HuaWeiChannelID = channelId
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAndroidXiaoMiChannelId 设置小米手机 MIUI 10 及以上的通知类别(Channel)适配字段
|
||||||
|
func (o *offlinePush) SetAndroidXiaoMiChannelId(channelId string) {
|
||||||
|
if o.androidInfo == nil {
|
||||||
|
o.androidInfo = &types.AndroidInfo{}
|
||||||
|
}
|
||||||
|
o.androidInfo.XiaoMiChannelID = channelId
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAndroidOppoChannelId 设置OPPO手机 Android 8.0 及以上的 NotificationChannel 通知适配字段
|
||||||
|
func (o *offlinePush) SetAndroidOppoChannelId(channelId string) {
|
||||||
|
if o.androidInfo == nil {
|
||||||
|
o.androidInfo = &types.AndroidInfo{}
|
||||||
|
}
|
||||||
|
o.androidInfo.OPPOChannelID = channelId
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAndroidGoogleChannelId 设置Google 手机 Android 8.0 及以上的通知渠道字段
|
||||||
|
func (o *offlinePush) SetAndroidGoogleChannelId(channelId string) {
|
||||||
|
if o.androidInfo == nil {
|
||||||
|
o.androidInfo = &types.AndroidInfo{}
|
||||||
|
}
|
||||||
|
o.androidInfo.GoogleChannelID = channelId
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAndroidVivoClassification 设置VIVO 手机推送消息分类,“0”代表运营消息,“1”代表系统消息,不填默认为1
|
||||||
|
func (o *offlinePush) SetAndroidVivoClassification(classification types.VivoClassification) {
|
||||||
|
if o.androidInfo == nil {
|
||||||
|
o.androidInfo = &types.AndroidInfo{}
|
||||||
|
}
|
||||||
|
o.androidInfo.VIVOClassification = int(classification)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAndroidHuaWeiImportance 设置华为推送通知消息分类
|
||||||
|
func (o *offlinePush) SetAndroidHuaWeiImportance(importance types.HuaWeiImportance) {
|
||||||
|
if o.androidInfo == nil {
|
||||||
|
o.androidInfo = &types.AndroidInfo{}
|
||||||
|
}
|
||||||
|
o.androidInfo.HuaWeiImportance = string(importance)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAndroidExtAsHuaweiIntentParam 设置在控制台配置华为推送为“打开应用内指定页面”的前提下,传“1”表示将透传内容 Ext 作为 Intent 的参数,“0”表示将透传内容 Ext 作为 Action 参数。不填默认为0。
|
||||||
|
func (o *offlinePush) SetAndroidExtAsHuaweiIntentParam(param types.HuaweiIntentParam) {
|
||||||
|
if o.androidInfo == nil {
|
||||||
|
o.androidInfo = &types.AndroidInfo{}
|
||||||
|
}
|
||||||
|
o.androidInfo.ExtAsHuaweiIntentParam = int(param)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetApnsBadgeMode 设置IOS徽章计数模式
|
||||||
|
func (o *offlinePush) SetApnsBadgeMode(badgeMode types.BadgeMode) {
|
||||||
|
if o.apnsInfo == nil {
|
||||||
|
o.apnsInfo = &types.ApnsInfo{}
|
||||||
|
}
|
||||||
|
o.apnsInfo.BadgeMode = int(badgeMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetApnsTitle 设置APNs推送的标题
|
||||||
|
func (o *offlinePush) SetApnsTitle(title string) {
|
||||||
|
if o.apnsInfo == nil {
|
||||||
|
o.apnsInfo = &types.ApnsInfo{}
|
||||||
|
}
|
||||||
|
o.apnsInfo.Title = title
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetApnsSubTitle 设置APNs推送的子标题
|
||||||
|
func (o *offlinePush) SetApnsSubTitle(subTitle string) {
|
||||||
|
if o.apnsInfo == nil {
|
||||||
|
o.apnsInfo = &types.ApnsInfo{}
|
||||||
|
}
|
||||||
|
o.apnsInfo.SubTitle = subTitle
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetApnsImage 设置APNs携带的图片地址
|
||||||
|
func (o *offlinePush) SetApnsImage(image string) {
|
||||||
|
if o.apnsInfo == nil {
|
||||||
|
o.apnsInfo = &types.ApnsInfo{}
|
||||||
|
}
|
||||||
|
o.apnsInfo.Image = image
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetApnsMutableContent 设置iOS10的推送扩展开关
|
||||||
|
func (o *offlinePush) SetApnsMutableContent(mutable types.MutableContent) {
|
||||||
|
if o.apnsInfo == nil {
|
||||||
|
o.apnsInfo = &types.ApnsInfo{}
|
||||||
|
}
|
||||||
|
o.apnsInfo.MutableContent = int(mutable)
|
||||||
|
}
|
330
internal/entity/user.go
Normal file
330
internal/entity/user.go
Normal file
@ -0,0 +1,330 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Email:1711788888@qq.com
|
||||||
|
* @Date: 2021/8/30 4:23 下午
|
||||||
|
* @Desc: 用户
|
||||||
|
*/
|
||||||
|
|
||||||
|
package entity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/core"
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/enum"
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
userId string
|
||||||
|
attrs map[string]interface{}
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUserId 设置用户账号
|
||||||
|
func (u *User) SetUserId(userId string) {
|
||||||
|
u.userId = userId
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserId 获取用户账号
|
||||||
|
func (u *User) GetUserId() string {
|
||||||
|
return u.userId
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNickname 设置昵称
|
||||||
|
func (u *User) SetNickname(nickname string) {
|
||||||
|
u.SetAttr(enum.StandardAttrNickname, nickname)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNickname 获取昵称
|
||||||
|
func (u *User) GetNickname() (nickname string, exist bool) {
|
||||||
|
var v interface{}
|
||||||
|
|
||||||
|
if v, exist = u.GetAttr(enum.StandardAttrNickname); exist {
|
||||||
|
nickname = v.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetGender 设置性别
|
||||||
|
func (u *User) SetGender(gender types.GenderType) {
|
||||||
|
u.SetAttr(enum.StandardAttrGender, gender)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGender 获取性别
|
||||||
|
func (u *User) GetGender() (gender types.GenderType, exist bool) {
|
||||||
|
var v interface{}
|
||||||
|
|
||||||
|
if v, exist = u.GetAttr(enum.StandardAttrGender); exist {
|
||||||
|
gender = types.GenderType(v.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBirthday 设置生日
|
||||||
|
func (u *User) SetBirthday(birthday time.Time) {
|
||||||
|
b, _ := strconv.Atoi(birthday.Format("20060102"))
|
||||||
|
u.SetAttr(enum.StandardAttrBirthday, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBirthday 获取昵称
|
||||||
|
func (u *User) GetBirthday() (birthday time.Time, exist bool) {
|
||||||
|
var v interface{}
|
||||||
|
|
||||||
|
if v, exist = u.GetAttr(enum.StandardAttrBirthday); exist {
|
||||||
|
if val := v.(string); val != "" {
|
||||||
|
birthday, _ = time.Parse("20060102", val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLocation 设置所在地
|
||||||
|
func (u *User) SetLocation(country uint32, province uint32, city uint32, region uint32) {
|
||||||
|
var (
|
||||||
|
str string
|
||||||
|
location = []uint32{country, province, city, region}
|
||||||
|
builder strings.Builder
|
||||||
|
)
|
||||||
|
|
||||||
|
builder.Grow(16)
|
||||||
|
|
||||||
|
for _, v := range location {
|
||||||
|
str = strconv.Itoa(int(v))
|
||||||
|
|
||||||
|
if len(str) > 4 {
|
||||||
|
u.SetError(enum.InvalidParamsCode, "invalid location params")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.WriteString(strings.Repeat("0", 4-len(str)))
|
||||||
|
builder.WriteString(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
u.SetAttr(enum.StandardAttrLocation, builder.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLocation 获取所在地
|
||||||
|
func (u *User) GetLocation() (country uint32, province uint32, city uint32, region uint32, exist bool) {
|
||||||
|
var v interface{}
|
||||||
|
|
||||||
|
if v, exist = u.attrs[enum.StandardAttrLocation]; exist {
|
||||||
|
str := v.(string)
|
||||||
|
|
||||||
|
if len(str) != 16 {
|
||||||
|
exist = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if c, err := strconv.Atoi(str[0:4]); err != nil || c < 0 {
|
||||||
|
exist = false
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
country = uint32(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c, err := strconv.Atoi(str[4:8]); err != nil || c < 0 {
|
||||||
|
exist = false
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
province = uint32(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c, err := strconv.Atoi(str[8:12]); err != nil || c < 0 {
|
||||||
|
exist = false
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
city = uint32(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c, err := strconv.Atoi(str[12:16]); err != nil || c < 0 {
|
||||||
|
exist = false
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
region = uint32(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSignature 设置个性签名
|
||||||
|
func (u *User) SetSignature(signature string) {
|
||||||
|
u.SetAttr(enum.StandardAttrSignature, signature)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSignature 获取个性签名
|
||||||
|
func (u *User) GetSignature() (signature string, exist bool) {
|
||||||
|
var v interface{}
|
||||||
|
|
||||||
|
if v, exist = u.GetAttr(enum.StandardAttrSignature); exist {
|
||||||
|
signature = v.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAllowType 设置加好友验证方式
|
||||||
|
func (u *User) SetAllowType(allowType types.AllowType) {
|
||||||
|
u.SetAttr(enum.StandardAttrAllowType, allowType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllowType 获取加好友验证方式
|
||||||
|
func (u *User) GetAllowType() (allowType types.AllowType, exist bool) {
|
||||||
|
var v interface{}
|
||||||
|
|
||||||
|
if v, exist = u.GetAttr(enum.StandardAttrAllowType); exist {
|
||||||
|
allowType = types.AllowType(v.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLanguage 设置语言
|
||||||
|
func (u *User) SetLanguage(language uint) {
|
||||||
|
u.SetAttr(enum.StandardAttrLanguage, language)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLanguage 获取语言
|
||||||
|
func (u *User) GetLanguage() (language uint, exist bool) {
|
||||||
|
var v interface{}
|
||||||
|
|
||||||
|
if v, exist = u.GetAttr(enum.StandardAttrLanguage); exist {
|
||||||
|
language = uint(v.(float64))
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAvatar 设置头像URL
|
||||||
|
func (u *User) SetAvatar(avatar string) {
|
||||||
|
u.SetAttr(enum.StandardAttrAvatar, avatar)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAvatar 获取头像URL
|
||||||
|
func (u *User) GetAvatar() (avatar string, exist bool) {
|
||||||
|
var v interface{}
|
||||||
|
|
||||||
|
if v, exist = u.GetAttr(enum.StandardAttrAvatar); exist {
|
||||||
|
avatar = v.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMsgSettings 设置消息设置
|
||||||
|
func (u *User) SetMsgSettings(settings uint) {
|
||||||
|
u.SetAttr(enum.StandardAttrMsgSettings, settings)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMsgSettings 获取消息设置
|
||||||
|
func (u *User) GetMsgSettings() (settings uint, exist bool) {
|
||||||
|
var v interface{}
|
||||||
|
|
||||||
|
if v, exist = u.GetAttr(enum.StandardAttrMsgSettings); exist {
|
||||||
|
settings = uint(v.(float64))
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAdminForbidType 设置管理员禁止加好友标识
|
||||||
|
func (u *User) SetAdminForbidType(forbidType types.AdminForbidType) {
|
||||||
|
u.SetAttr(enum.StandardAttrAdminForbidType, forbidType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAdminForbidType 获取管理员禁止加好友标识
|
||||||
|
func (u *User) GetAdminForbidType() (forbidType types.AdminForbidType, exist bool) {
|
||||||
|
var v interface{}
|
||||||
|
|
||||||
|
if v, exist = u.GetAttr(enum.StandardAttrAdminForbidType); exist {
|
||||||
|
forbidType = types.AdminForbidType(v.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLevel 设置等级
|
||||||
|
func (u *User) SetLevel(level uint) {
|
||||||
|
u.SetAttr(enum.StandardAttrLevel, level)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLevel 获取等级
|
||||||
|
func (u *User) GetLevel() (level uint, exist bool) {
|
||||||
|
var v interface{}
|
||||||
|
|
||||||
|
if v, exist = u.GetAttr(enum.StandardAttrLevel); exist {
|
||||||
|
level = uint(v.(float64))
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRole 设置角色
|
||||||
|
func (u *User) SetRole(role uint) {
|
||||||
|
u.SetAttr(enum.StandardAttrRole, role)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRole 获取角色
|
||||||
|
func (u *User) GetRole() (role uint, exist bool) {
|
||||||
|
var v interface{}
|
||||||
|
|
||||||
|
if v, exist = u.GetAttr(enum.StandardAttrRole); exist {
|
||||||
|
role = uint(v.(float64))
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCustomAttr 设置自定义属性
|
||||||
|
func (u *User) SetCustomAttr(name string, value interface{}) {
|
||||||
|
u.SetAttr(fmt.Sprintf("%s_%s", enum.CustomAttrPrefix, name), value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCustomAttr 获取自定义属性
|
||||||
|
func (u *User) GetCustomAttr(name string) (val interface{}, exist bool) {
|
||||||
|
val, exist = u.GetAttr(fmt.Sprintf("%s_%s", enum.CustomAttrPrefix, name))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValid 检测用户是否有效
|
||||||
|
func (u *User) IsValid() bool {
|
||||||
|
return u.err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetError 设置异常错误
|
||||||
|
func (u *User) SetError(code int, message string) {
|
||||||
|
if code != enum.SuccessCode {
|
||||||
|
u.err = core.NewError(code, message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetError 获取异常错误
|
||||||
|
func (u *User) GetError() error {
|
||||||
|
return u.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAttr 设置属性
|
||||||
|
func (u *User) SetAttr(name string, value interface{}) {
|
||||||
|
if u.attrs == nil {
|
||||||
|
u.attrs = make(map[string]interface{})
|
||||||
|
}
|
||||||
|
u.attrs[name] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAttr 获取属性
|
||||||
|
func (u *User) GetAttr(name string) (value interface{}, exist bool) {
|
||||||
|
value, exist = u.attrs[name]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAttrs 获取所有属性
|
||||||
|
func (u *User) GetAttrs() map[string]interface{} {
|
||||||
|
return u.attrs
|
||||||
|
}
|
16
internal/enum/code.go
Normal file
16
internal/enum/code.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* @Author: wanglin
|
||||||
|
* @Author: wanglin@vspn.com
|
||||||
|
* @Date: 2021/11/3 10:45
|
||||||
|
* @Desc: TODO
|
||||||
|
*/
|
||||||
|
|
||||||
|
package enum
|
||||||
|
|
||||||
|
const (
|
||||||
|
SuccessActionStatus = "OK" // 成功状态
|
||||||
|
FailActionStatus = "FAIL" // 失败状态
|
||||||
|
SuccessCode = 0 // 成功
|
||||||
|
InvalidParamsCode = -1 // 无效参数(自定义)
|
||||||
|
InvalidResponseCode = -2 // 无效响应(自定义)
|
||||||
|
)
|
95
internal/enum/enum.go
Normal file
95
internal/enum/enum.go
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Email:1711788888@qq.com
|
||||||
|
* @Date: 2021/8/28 1:14 上午
|
||||||
|
* @Desc: TODO
|
||||||
|
*/
|
||||||
|
|
||||||
|
package enum
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// 消息类型
|
||||||
|
MsgText = "TIMTextElem" // 消息元素
|
||||||
|
MsgLocation = "TIMLocationElem" // 地理位置消息元素
|
||||||
|
MsgFace = "TIMFaceElem" // 表情消息元素
|
||||||
|
MsgCustom = "TIMCustomElem" // 自定义消息元素
|
||||||
|
MsgSound = "TIMSoundElem" // 语音消息元素
|
||||||
|
MsgImage = "TIMImageElem" // 图像消息元素
|
||||||
|
MsgFile = "TIMFileElem" // 文件消息元素
|
||||||
|
MsgVideo = "TIMVideoFileElem" // 视频消息元素
|
||||||
|
|
||||||
|
// 图片格式
|
||||||
|
ImageFormatJPG = 1 // JPG格式
|
||||||
|
ImageFormatGIF = 2 // GIF格式
|
||||||
|
ImageFormatPNG = 3 // PNG格式
|
||||||
|
ImageFormatBMP = 4 // BMP格式
|
||||||
|
ImageFormatOTHER = 255 // 其他格式
|
||||||
|
|
||||||
|
// 图片类型
|
||||||
|
ImageTypeOriginal = 1 // 原图
|
||||||
|
ImageTypePic = 2 // 大图
|
||||||
|
ImageTypeThumb = 3 // 缩略图
|
||||||
|
|
||||||
|
// 标准资料字段
|
||||||
|
StandardAttrNickname = "Tag_Profile_IM_Nick" // 昵称
|
||||||
|
StandardAttrGender = "Tag_Profile_IM_Gender" // 性别
|
||||||
|
StandardAttrBirthday = "Tag_Profile_IM_BirthDay" // 生日
|
||||||
|
StandardAttrLocation = "Tag_Profile_IM_Location" // 所在地
|
||||||
|
StandardAttrSignature = "Tag_Profile_IM_SelfSignature" // 个性签名
|
||||||
|
StandardAttrAllowType = "Tag_Profile_IM_AllowType" // 加好友验证方式
|
||||||
|
StandardAttrLanguage = "Tag_Profile_IM_Language" // 语言
|
||||||
|
StandardAttrAvatar = "Tag_Profile_IM_Image" // 头像URL
|
||||||
|
StandardAttrMsgSettings = "Tag_Profile_IM_MsgSettings" // 消息设置
|
||||||
|
StandardAttrAdminForbidType = "Tag_Profile_IM_AdminForbidType" // 管理员禁止加好友标识
|
||||||
|
StandardAttrLevel = "Tag_Profile_IM_Level" // 等级
|
||||||
|
StandardAttrRole = "Tag_Profile_IM_Role" // 角色
|
||||||
|
|
||||||
|
// 自定义属性前缀
|
||||||
|
CustomAttrPrefix = "Tag_Profile_Custom" // 自定义属性前缀
|
||||||
|
|
||||||
|
// 性别类型
|
||||||
|
GenderTypeUnknown types.GenderType = "Gender_Type_Unknown" // 没设置性别
|
||||||
|
GenderTypeFemale types.GenderType = "Gender_Type_Female" // 女性
|
||||||
|
GenderTypeMale types.GenderType = "Gender_Type_Male" // 男性
|
||||||
|
|
||||||
|
// 加好友验证方式
|
||||||
|
AllowTypeNeedConfirm types.AllowType = "AllowType_Type_NeedConfirm" // 需要经过自己确认对方才能添加自己为好友
|
||||||
|
AllowTypeAllowAny types.AllowType = "AllowType_Type_AllowAny" // 允许任何人添加自己为好友
|
||||||
|
AllowTypeDenyAny types.AllowType = "AllowType_Type_DenyAny" // 不允许任何人添加自己为好友
|
||||||
|
|
||||||
|
// 管理员禁止加好友标识类型
|
||||||
|
AdminForbidTypeNone types.AdminForbidType = "AdminForbid_Type_None" // 默认值,允许加好友
|
||||||
|
AdminForbidTypeSendOut types.AdminForbidType = "AdminForbid_Type_SendOut" // 禁止该用户发起加好友请求
|
||||||
|
|
||||||
|
// 同步至其他设备
|
||||||
|
SyncOtherMachineYes types.SyncOtherMachine = 1 // 把消息同步到From_Account在线终端和漫游上
|
||||||
|
SyncOtherMachineNo types.SyncOtherMachine = 2 // 消息不同步至From_Account
|
||||||
|
|
||||||
|
// 推送标识
|
||||||
|
PushFlagYes types.PushFlag = 0 // 正常推送
|
||||||
|
PushFlagNo types.PushFlag = 1 // 不离线推送
|
||||||
|
|
||||||
|
// 华为推送通知消息分类
|
||||||
|
HuaWeiImportanceLow types.HuaWeiImportance = "LOW" // LOW类消息
|
||||||
|
HuaWeiImportanceNormal types.HuaWeiImportance = "NORMAL" // NORMAL类消息
|
||||||
|
|
||||||
|
// 华为推送为“打开应用内指定页面”的前提下透传参数行为
|
||||||
|
HuaweiIntentParamAction types.HuaweiIntentParam = 0 // 将透传内容Ext作为Action参数
|
||||||
|
HuaweiIntentParamIntent types.HuaweiIntentParam = 1 // 将透传内容Ext作为Intent参数
|
||||||
|
|
||||||
|
// VIVO手机推送消息分类
|
||||||
|
VivoClassificationOperation types.VivoClassification = 0 // 运营类消息
|
||||||
|
VivoClassificationSystem types.VivoClassification = 1 // 系统类消息
|
||||||
|
|
||||||
|
// IOS徽章计数模式
|
||||||
|
BadgeModeNormal types.BadgeMode = 0 // 本条消息需要计数
|
||||||
|
BadgeModeIgnore types.BadgeMode = 1 // 本条消息不需要计数
|
||||||
|
|
||||||
|
// iOS10的推送扩展开关
|
||||||
|
MutableContentNormal types.MutableContent = 0 // 关闭iOS10的推送扩展
|
||||||
|
MutableContentEnable types.MutableContent = 1 // 开启iOS10的推送扩展
|
||||||
|
)
|
63
internal/random/random.go
Normal file
63
internal/random/random.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Author:1711788888@qq.com
|
||||||
|
* @Date: 2021/5/12 10:35
|
||||||
|
* @Desc: 随机数类库
|
||||||
|
*/
|
||||||
|
|
||||||
|
package random
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
AlphaStr = iota // 字母字
|
||||||
|
AlphaLowerStr // 小写字母
|
||||||
|
AlphaUpperStr // 大写字母
|
||||||
|
NumericStr // 数字
|
||||||
|
NoZeroNumericStr // 无0数字
|
||||||
|
)
|
||||||
|
|
||||||
|
// GenStr 生成指定长度的字符串
|
||||||
|
func GenStr(mode, length int) string {
|
||||||
|
var (
|
||||||
|
pos int
|
||||||
|
lastStr string
|
||||||
|
seedStr string
|
||||||
|
)
|
||||||
|
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
switch mode {
|
||||||
|
case AlphaStr:
|
||||||
|
seedStr = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
case AlphaLowerStr:
|
||||||
|
seedStr = "abcdefghijklmnopqrstuvwxyz"
|
||||||
|
case AlphaUpperStr:
|
||||||
|
seedStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
case NumericStr:
|
||||||
|
seedStr = "0123456789"
|
||||||
|
case NoZeroNumericStr:
|
||||||
|
seedStr = "123456789"
|
||||||
|
}
|
||||||
|
|
||||||
|
seedLen := len(seedStr)
|
||||||
|
for i := 0; i < length; i++ {
|
||||||
|
pos = rand.Intn(seedLen)
|
||||||
|
lastStr += seedStr[pos : pos+1]
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastStr
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenNumeric 生成指定范围的数字
|
||||||
|
func GenNumeric(min int, max int) int {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
|
||||||
|
if min < max {
|
||||||
|
return rand.Intn(max-min) + min
|
||||||
|
} else {
|
||||||
|
return rand.Intn(min-max) + max
|
||||||
|
}
|
||||||
|
}
|
30
internal/sign/base64.go
Normal file
30
internal/sign/base64.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Author:1711788888@qq.com
|
||||||
|
* @Date: 2021/5/27 19:15
|
||||||
|
* @Desc: BASE64
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sign
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// base64Encode base64 encode a string.
|
||||||
|
func base64Encode(data []byte) string {
|
||||||
|
str := base64.StdEncoding.EncodeToString(data)
|
||||||
|
str = strings.Replace(str, "+", "*", -1)
|
||||||
|
str = strings.Replace(str, "/", "-", -1)
|
||||||
|
str = strings.Replace(str, "=", "_", -1)
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
// base64Decode base64 decode a string.
|
||||||
|
func base64Decode(str string) ([]byte, error) {
|
||||||
|
str = strings.Replace(str, "_", "=", -1)
|
||||||
|
str = strings.Replace(str, "-", "/", -1)
|
||||||
|
str = strings.Replace(str, "*", "+", -1)
|
||||||
|
return base64.StdEncoding.DecodeString(str)
|
||||||
|
}
|
178
internal/sign/sign.go
Normal file
178
internal/sign/sign.go
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Author:1711788888@qq.com
|
||||||
|
* @Date: 2021/5/27 19:11
|
||||||
|
* @Desc: Transmission signature.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sign
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/zlib"
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GenUserSig gen a user sign.
|
||||||
|
func GenUserSig(sdkAppId int, key string, userid string, expire int) (string, error) {
|
||||||
|
return genUserSig(sdkAppId, key, userid, expire, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenPrivateMapKey gen a private map.
|
||||||
|
func GenPrivateMapKey(sdkAppId int, key string, userid string, expire int, roomId uint32, privilegeMap uint32) (string, error) {
|
||||||
|
var userBuf []byte = genUserBuf(userid, sdkAppId, roomId, expire, privilegeMap, 0, "")
|
||||||
|
return genUserSig(sdkAppId, key, userid, expire, userBuf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenPrivateMapKeyWithRoomId gen a private map with room id.
|
||||||
|
func GenPrivateMapKeyWithRoomId(sdkAppId int, key string, userid string, expire int, roomId string, privilegeMap uint32) (string, error) {
|
||||||
|
var userBuf []byte = genUserBuf(userid, sdkAppId, 0, expire, privilegeMap, 0, roomId)
|
||||||
|
return genUserSig(sdkAppId, key, userid, expire, userBuf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// genUserBuf gen a user buffer.
|
||||||
|
func genUserBuf(account string, dwSdkappid int, dwAuthID uint32,
|
||||||
|
dwExpTime int, dwPrivilegeMap uint32, dwAccountType uint32, roomStr string) []byte {
|
||||||
|
|
||||||
|
offset := 0
|
||||||
|
length := 1 + 2 + len(account) + 20 + len(roomStr)
|
||||||
|
if len(roomStr) > 0 {
|
||||||
|
length = length + 2
|
||||||
|
}
|
||||||
|
|
||||||
|
userBuf := make([]byte, length)
|
||||||
|
|
||||||
|
// ver
|
||||||
|
if len(roomStr) > 0 {
|
||||||
|
userBuf[offset] = 1
|
||||||
|
} else {
|
||||||
|
userBuf[offset] = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
offset++
|
||||||
|
userBuf[offset] = (byte)((len(account) & 0xFF00) >> 8)
|
||||||
|
offset++
|
||||||
|
userBuf[offset] = (byte)(len(account) & 0x00FF)
|
||||||
|
offset++
|
||||||
|
|
||||||
|
for ; offset < len(account)+3; offset++ {
|
||||||
|
userBuf[offset] = account[offset-3]
|
||||||
|
}
|
||||||
|
|
||||||
|
// dwSdkAppid
|
||||||
|
userBuf[offset] = (byte)((int64(dwSdkappid) & 0xFF000000) >> 24)
|
||||||
|
offset++
|
||||||
|
userBuf[offset] = (byte)((dwSdkappid & 0x00FF0000) >> 16)
|
||||||
|
offset++
|
||||||
|
userBuf[offset] = (byte)((dwSdkappid & 0x0000FF00) >> 8)
|
||||||
|
offset++
|
||||||
|
userBuf[offset] = (byte)(dwSdkappid & 0x000000FF)
|
||||||
|
offset++
|
||||||
|
|
||||||
|
// dwAuthId
|
||||||
|
userBuf[offset] = (byte)((dwAuthID & 0xFF000000) >> 24)
|
||||||
|
offset++
|
||||||
|
userBuf[offset] = (byte)((dwAuthID & 0x00FF0000) >> 16)
|
||||||
|
offset++
|
||||||
|
userBuf[offset] = (byte)((dwAuthID & 0x0000FF00) >> 8)
|
||||||
|
offset++
|
||||||
|
userBuf[offset] = (byte)(dwAuthID & 0x000000FF)
|
||||||
|
offset++
|
||||||
|
|
||||||
|
// dwExpTime now+300;
|
||||||
|
currTime := time.Now().Unix()
|
||||||
|
var expire = currTime + int64(dwExpTime)
|
||||||
|
userBuf[offset] = (byte)((expire & 0xFF000000) >> 24)
|
||||||
|
offset++
|
||||||
|
userBuf[offset] = (byte)((expire & 0x00FF0000) >> 16)
|
||||||
|
offset++
|
||||||
|
userBuf[offset] = (byte)((expire & 0x0000FF00) >> 8)
|
||||||
|
offset++
|
||||||
|
userBuf[offset] = (byte)(expire & 0x000000FF)
|
||||||
|
offset++
|
||||||
|
|
||||||
|
// dwPrivilegeMap
|
||||||
|
userBuf[offset] = (byte)((dwPrivilegeMap & 0xFF000000) >> 24)
|
||||||
|
offset++
|
||||||
|
userBuf[offset] = (byte)((dwPrivilegeMap & 0x00FF0000) >> 16)
|
||||||
|
offset++
|
||||||
|
userBuf[offset] = (byte)((dwPrivilegeMap & 0x0000FF00) >> 8)
|
||||||
|
offset++
|
||||||
|
userBuf[offset] = (byte)(dwPrivilegeMap & 0x000000FF)
|
||||||
|
offset++
|
||||||
|
|
||||||
|
// dwAccountType
|
||||||
|
userBuf[offset] = (byte)((dwAccountType & 0xFF000000) >> 24)
|
||||||
|
offset++
|
||||||
|
userBuf[offset] = (byte)((dwAccountType & 0x00FF0000) >> 16)
|
||||||
|
offset++
|
||||||
|
userBuf[offset] = (byte)((dwAccountType & 0x0000FF00) >> 8)
|
||||||
|
offset++
|
||||||
|
userBuf[offset] = (byte)(dwAccountType & 0x000000FF)
|
||||||
|
offset++
|
||||||
|
|
||||||
|
if len(roomStr) > 0 {
|
||||||
|
userBuf[offset] = (byte)((len(roomStr) & 0xFF00) >> 8)
|
||||||
|
offset++
|
||||||
|
userBuf[offset] = (byte)(len(roomStr) & 0x00FF)
|
||||||
|
offset++
|
||||||
|
|
||||||
|
for ; offset < length; offset++ {
|
||||||
|
userBuf[offset] = roomStr[offset-(length-len(roomStr))]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return userBuf
|
||||||
|
}
|
||||||
|
|
||||||
|
// hmacSha256 encrypt with HMAC SHA256.
|
||||||
|
func hmacSha256(sdkAppId int, key string, identifier string, currTime int64, expire int, base64UserBuf *string) string {
|
||||||
|
var contentToBeSigned string
|
||||||
|
contentToBeSigned = "TLS.identifier:" + identifier + "\n"
|
||||||
|
contentToBeSigned += "TLS.sdkappid:" + strconv.Itoa(sdkAppId) + "\n"
|
||||||
|
contentToBeSigned += "TLS.time:" + strconv.FormatInt(currTime, 10) + "\n"
|
||||||
|
contentToBeSigned += "TLS.expire:" + strconv.Itoa(expire) + "\n"
|
||||||
|
if nil != base64UserBuf {
|
||||||
|
contentToBeSigned += "TLS.userbuf:" + *base64UserBuf + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
h := hmac.New(sha256.New, []byte(key))
|
||||||
|
h.Write([]byte(contentToBeSigned))
|
||||||
|
return base64.StdEncoding.EncodeToString(h.Sum(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// genUserSig gen a sign
|
||||||
|
func genUserSig(sdkAppId int, key string, identifier string, expire int, userBuf []byte) (string, error) {
|
||||||
|
currTime := time.Now().Unix()
|
||||||
|
var sigDoc map[string]interface{}
|
||||||
|
sigDoc = make(map[string]interface{})
|
||||||
|
sigDoc["TLS.ver"] = "2.0"
|
||||||
|
sigDoc["TLS.identifier"] = identifier
|
||||||
|
sigDoc["TLS.sdkappid"] = sdkAppId
|
||||||
|
sigDoc["TLS.expire"] = expire
|
||||||
|
sigDoc["TLS.time"] = currTime
|
||||||
|
var base64UserBuf string
|
||||||
|
if nil != userBuf {
|
||||||
|
base64UserBuf = base64.StdEncoding.EncodeToString(userBuf)
|
||||||
|
sigDoc["TLS.userbuf"] = base64UserBuf
|
||||||
|
sigDoc["TLS.sig"] = hmacSha256(sdkAppId, key, identifier, currTime, expire, &base64UserBuf)
|
||||||
|
} else {
|
||||||
|
sigDoc["TLS.sig"] = hmacSha256(sdkAppId, key, identifier, currTime, expire, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(sigDoc)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
w := zlib.NewWriter(&b)
|
||||||
|
_, _ = w.Write(data)
|
||||||
|
_ = w.Close()
|
||||||
|
return base64Encode(b.Bytes()), nil
|
||||||
|
}
|
30
internal/types/interface.go
Normal file
30
internal/types/interface.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Email:1711788888@qq.com
|
||||||
|
* @Date: 2021/8/27 12:54 下午
|
||||||
|
* @Desc: TODO
|
||||||
|
*/
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
type BaseRespInterface interface {
|
||||||
|
GetErrorCode() int
|
||||||
|
GetErrorInfo() string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *BaseResp) GetErrorCode() int {
|
||||||
|
return r.ErrorCode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *BaseResp) GetErrorInfo() string {
|
||||||
|
return r.ErrorInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
type ActionBaseRespInterface interface {
|
||||||
|
BaseRespInterface
|
||||||
|
GetActionStatus() string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ActionBaseResp) GetActionStatus() string {
|
||||||
|
return r.ActionStatus
|
||||||
|
}
|
171
internal/types/types.go
Normal file
171
internal/types/types.go
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Author:1711788888@qq.com
|
||||||
|
* @Date: 2021/5/28 19:24
|
||||||
|
* @Desc: TODO
|
||||||
|
*/
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
type (
|
||||||
|
BaseResp struct {
|
||||||
|
ErrorCode int `json:"ErrorCode"`
|
||||||
|
ErrorInfo string `json:"ErrorInfo"`
|
||||||
|
ErrorDisplay string `json:"ErrorDisplay,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
ActionBaseResp struct {
|
||||||
|
BaseResp
|
||||||
|
ActionStatus string `json:"ActionStatus"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AndroidInfo Android离线推送消息
|
||||||
|
AndroidInfo struct {
|
||||||
|
Sound string `json:"Sound,omitempty"` // (选填)Android 离线推送声音文件路径。
|
||||||
|
HuaWeiChannelID string `json:"HuaWeiChannelID,omitempty"` // (选填)华为手机 EMUI 10.0 及以上的通知渠道字段。该字段不为空时,会覆盖控制台配置的 ChannelID 值;该字段为空时,不会覆盖控制台配置的 ChannelID 值。
|
||||||
|
XiaoMiChannelID string `json:"XiaoMiChannelID,omitempty"` // (选填)小米手机 MIUI 10 及以上的通知类别(Channel)适配字段。该字段不为空时,会覆盖控制台配置的 ChannelID 值;该字段为空时,不会覆盖控制台配置的 ChannelID 值。
|
||||||
|
OPPOChannelID string `json:"OPPOChannelID,omitempty"` // (选填)OPPO 手机 Android 8.0 及以上的 NotificationChannel 通知适配字段。该字段不为空时,会覆盖控制台配置的 ChannelID 值;该字段为空时,不会覆盖控制台配置的 ChannelID 值。
|
||||||
|
GoogleChannelID string `json:"GoogleChannelID,omitempty"` // (选填)Google 手机 Android 8.0 及以上的通知渠道字段。Google 推送新接口(上传证书文件)支持 channel id,旧接口(填写服务器密钥)不支持。
|
||||||
|
VIVOClassification int `json:"VIVOClassification,omitempty"` // (选填)VIVO 手机推送消息分类,“0”代表运营消息,“1”代表系统消息,不填默认为1。
|
||||||
|
HuaWeiImportance string `json:"HuaWeiImportance,omitempty"` // (选填)华为推送通知消息分类,取值为 LOW、NORMAL,不填默认为 NORMAL。
|
||||||
|
ExtAsHuaweiIntentParam int `json:"ExtAsHuaweiIntentParam,omitempty"` // (选填)在控制台配置华为推送为“打开应用内指定页面”的前提下,传“1”表示将透传内容 Ext 作为 Intent 的参数,“0”表示将透传内容 Ext 作为 Action 参数。不填默认为0。两种传参区别可参见 华为推送文档。
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApnsInfo IOS离线推送消息
|
||||||
|
ApnsInfo struct {
|
||||||
|
BadgeMode int `json:"BadgeMode,omitempty"` // (选填)这个字段缺省或者为0表示需要计数,为1表示本条消息不需要计数,即右上角图标数字不增加。
|
||||||
|
Title string `json:"Title,omitempty"` // (选填)该字段用于标识 APNs 推送的标题,若填写则会覆盖最上层 Title。
|
||||||
|
SubTitle string `json:"SubTitle,omitempty"` // (选填)该字段用于标识 APNs 推送的子标题。
|
||||||
|
Image string `json:"Image,omitempty"` // (选填)该字段用于标识 APNs 携带的图片地址,当客户端拿到该字段时,可以通过下载图片资源的方式将图片展示在弹窗上。
|
||||||
|
MutableContent int `json:"MutableContent,omitempty"` // (选填)为1表示开启 iOS 10 的推送扩展,默认为0。
|
||||||
|
}
|
||||||
|
|
||||||
|
// OfflinePushInfo 离线推送消息
|
||||||
|
OfflinePushInfo struct {
|
||||||
|
PushFlag int `json:"PushFlag,omitempty"` // (选填)推送标识。0表示推送,1表示不离线推送。
|
||||||
|
Title string `json:"Title,omitempty"` // (选填)离线推送标题。该字段为 iOS 和 Android 共用。
|
||||||
|
Desc string `json:"Desc,omitempty"` // (选填)离线推送内容。该字段会覆盖上面各种消息元素 TIMMsgElement 的离线推送展示文本。若发送的消息只有一个 TIMCustomElem 自定义消息元素,该 Desc 字段会覆盖 TIMCustomElem 中的 Desc 字段。如果两个 Desc 字段都不填,将收不到该自定义消息的离线推送。
|
||||||
|
Ext string `json:"Ext,omitempty"` // (选填)离线推送透传内容。由于国内各 Android 手机厂商的推送平台要求各不一样,请保证此字段为 JSON 格式,否则可能会导致收不到某些厂商的离线推送。
|
||||||
|
AndroidInfo *AndroidInfo `json:"AndroidInfo,omitempty"` // (选填)Android 离线推送消息
|
||||||
|
ApnsInfo *ApnsInfo `json:"ApnsInfo,omitempty"` // (选填)IOS离线推送消息
|
||||||
|
}
|
||||||
|
|
||||||
|
// MsgBody 消息内容
|
||||||
|
MsgBody struct {
|
||||||
|
MsgType string `json:"MsgType"`
|
||||||
|
MsgContent interface{} `json:"MsgContent"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TagPair 标签对
|
||||||
|
TagPair struct {
|
||||||
|
Tag string `json:"Tag"` // 标签
|
||||||
|
Value interface{} `json:"Value"` // 标签值
|
||||||
|
}
|
||||||
|
|
||||||
|
// MsgTextContent 文本消息内容
|
||||||
|
MsgTextContent struct {
|
||||||
|
Text string `json:"Text"` // (必填)消息内容。当接收方为 iOS 或 Android 后台在线时,作为离线推送的文本展示。
|
||||||
|
}
|
||||||
|
|
||||||
|
// MsgLocationContent 地理位置消息元素
|
||||||
|
MsgLocationContent struct {
|
||||||
|
Desc string `json:"Desc"` // (必填)地理位置描述信息
|
||||||
|
Latitude float64 `json:"Latitude"` // (必填)纬度
|
||||||
|
Longitude float64 `json:"Longitude"` // (必填)经度
|
||||||
|
}
|
||||||
|
|
||||||
|
// MsgFaceContent 表情消息元素
|
||||||
|
MsgFaceContent struct {
|
||||||
|
Index int `json:"Index"` // (必填)表情索引,用户自定义
|
||||||
|
Data string `json:"Data"` // (选填)额外数据
|
||||||
|
}
|
||||||
|
|
||||||
|
// MsgCustomContent 自定义消息元素
|
||||||
|
MsgCustomContent struct {
|
||||||
|
Desc string `json:"Desc"` // (选填)自定义消息描述信息。当接收方为 iOS 或 Android 后台在线时,做离线推送文本展示。 若发送自定义消息的同时设置了 OfflinePushInfo.Desc 字段,此字段会被覆盖,请优先填 OfflinePushInfo.Desc 字段。
|
||||||
|
Data string `json:"Data"` // (必填)自定义消息数据。 不作为 APNs 的 payload 字段下发,故从 payload 中无法获取 Data 字段
|
||||||
|
Ext string `json:"Ext"` // (选填)扩展字段。当接收方为 iOS 系统且应用处在后台时,此字段作为 APNs 请求包 Payloads 中的 Ext 键值下发,Ext 的协议格式由业务方确定,APNs 只做透传。
|
||||||
|
Sound string `json:"Sound"` // (选填)自定义 APNs 推送铃音。
|
||||||
|
}
|
||||||
|
|
||||||
|
// MsgSoundContent 语音消息元素
|
||||||
|
MsgSoundContent struct {
|
||||||
|
UUID string `json:"UUID"` // (必填)语音的唯一标识,类型为 String。客户端用于索引语音的键值。无法通过该字段下载相应的语音。若需要获取该语音,请升级 IM SDK 版本至4.X。
|
||||||
|
Url string `json:"Url"` // (必填)语音下载地址,可通过该 URL 地址直接下载相应语音
|
||||||
|
Size int `json:"Size"` // (必填)语音数据大小,单位:字节。
|
||||||
|
Second int `json:"Second"` // (必填)语音时长,单位:秒。
|
||||||
|
DownloadFlag int `json:"Download_Flag"` // (必填)语音下载方式标记。目前 Download_Flag 取值只能为2,表示可通过Url字段值的 URL 地址直接下载语音。
|
||||||
|
}
|
||||||
|
|
||||||
|
// MsgImageContent 图像消息元素
|
||||||
|
MsgImageContent struct {
|
||||||
|
UUID string `json:"UUID"` // (必填)图片序列号。后台用于索引图片的键值。
|
||||||
|
ImageFormat int `json:"ImageFormat"` // (必填)图片格式。JPG = 1,GIF = 2,PNG = 3,BMP = 4,其他 = 255。
|
||||||
|
ImageInfos []*ImageInfo `json:"ImageInfoArray"` // (必填)原图、缩略图或者大图下载信息。
|
||||||
|
}
|
||||||
|
|
||||||
|
// MsgFileContent 文件消息元素
|
||||||
|
MsgFileContent struct {
|
||||||
|
Url string `json:"Url"` // (必填)文件下载地址,可通过该 URL 地址直接下载相应文件
|
||||||
|
UUID string `json:"UUID"` // (必填)文件的唯一标识,客户端用于索引文件的键值。
|
||||||
|
FileSize int `json:"FileSize"` // (必填)文件数据大小,单位:字节
|
||||||
|
FileName string `json:"FileName"` // (必填)文件名称
|
||||||
|
DownloadFlag int `json:"Download_Flag"` // (必填)文件下载方式标记。目前 Download_Flag 取值只能为2,表示可通过Url字段值的 URL 地址直接下载文件。
|
||||||
|
}
|
||||||
|
|
||||||
|
// MsgVideoContent 视频消息元素
|
||||||
|
MsgVideoContent struct {
|
||||||
|
VideoUUID string `json:"VideoUUID"` // (必填)视频的唯一标识,客户端用于索引视频的键值。
|
||||||
|
VideoUrl string `json:"VideoUrl"` // (必填)视频下载地址。可通过该 URL 地址直接下载相应视频
|
||||||
|
VideoSize int `json:"VideoSize"` // (必填)视频数据大小,单位:字节
|
||||||
|
VideoSecond int `json:"VideoSecond"` // (必填)视频时长,单位:秒
|
||||||
|
VideoFormat string `json:"VideoFormat"` // (必填)视频格式,例如 mp4
|
||||||
|
VideoDownloadFlag int `json:"VideoDownloadFlag"` // (必填)视频下载方式标记。目前 VideoDownloadFlag 取值只能为2,表示可通过VideoUrl字段值的 URL 地址直接下载视频。
|
||||||
|
ThumbUrl string `json:"ThumbUrl"` // (必填)视频缩略图下载地址。可通过该 URL 地址直接下载相应视频缩略图。
|
||||||
|
ThumbUUID string `json:"ThumbUUID"` // (必填)视频缩略图的唯一标识,客户端用于索引视频缩略图的键值。
|
||||||
|
ThumbSize int `json:"ThumbSize"` // (必填)缩略图大小,单位:字节
|
||||||
|
ThumbWidth int `json:"ThumbWidth"` // (必填)缩略图宽度
|
||||||
|
ThumbHeight int `json:"ThumbHeight"` // (必填)缩略图高度
|
||||||
|
ThumbFormat string `json:"ThumbFormat"` // (必填)缩略图格式,例如 JPG、BMP 等
|
||||||
|
ThumbDownloadFlag int `json:"ThumbDownloadFlag"` // (必填)视频缩略图下载方式标记。目前 ThumbDownloadFlag 取值只能为2,表示可通过ThumbUrl字段值的 URL 地址直接下载视频缩略图。
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageInfo 图片下载信息
|
||||||
|
ImageInfo struct {
|
||||||
|
Type int `json:"Type"` // (必填)图片类型: 1-原图,2-大图,3-缩略图。
|
||||||
|
Size int `json:"Size"` // (必填)图片数据大小,单位:字节。
|
||||||
|
Width int `json:"Width"` // (必填)图片宽度。
|
||||||
|
Height int `json:"Height"` // (必填)图片高度。
|
||||||
|
Url string `json:"URL"` // (必填)图片下载地址。
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenderType 性别类型
|
||||||
|
GenderType string
|
||||||
|
|
||||||
|
// AllowType 加好友验证方式
|
||||||
|
AllowType string
|
||||||
|
|
||||||
|
// AdminForbidType 管理员禁止加好友标识类型
|
||||||
|
AdminForbidType string
|
||||||
|
|
||||||
|
// SyncOtherMachine 同步至其他设备
|
||||||
|
SyncOtherMachine int
|
||||||
|
|
||||||
|
// PushFlag 推送标识
|
||||||
|
PushFlag int
|
||||||
|
|
||||||
|
// HuaWeiImportance 华为推送通知消息分类
|
||||||
|
HuaWeiImportance string
|
||||||
|
|
||||||
|
// HuaweiIntentParam 华为推送为“打开应用内指定页面”的前提下透传参数行为
|
||||||
|
HuaweiIntentParam int
|
||||||
|
|
||||||
|
// VivoClassification VIVO手机推送消息分类
|
||||||
|
VivoClassification int
|
||||||
|
|
||||||
|
// BadgeMode IOS徽章计数模式
|
||||||
|
BadgeMode int
|
||||||
|
|
||||||
|
// MutableContent IOS10的推送扩展开关
|
||||||
|
MutableContent int
|
||||||
|
)
|
83
mute/api.go
Normal file
83
mute/api.go
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Email:1711788888@qq.com
|
||||||
|
* @Date: 2021/8/30 2:41 上午
|
||||||
|
* @Desc: 全局禁言管理
|
||||||
|
*/
|
||||||
|
|
||||||
|
package mute
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/core"
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
service = "openconfigsvr"
|
||||||
|
commandSetNoSpeaking = "setnospeaking"
|
||||||
|
commandGetNoSpeaking = "getnospeaking"
|
||||||
|
)
|
||||||
|
|
||||||
|
type API interface {
|
||||||
|
// SetNoSpeaking 设置全局禁言
|
||||||
|
// 设置帐号的单聊消息全局禁言。
|
||||||
|
// 设置帐号的群组消息全局禁言。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/4230
|
||||||
|
SetNoSpeaking(userId string, privateMuteTime, groupMuteTime *uint) (err error)
|
||||||
|
|
||||||
|
// GetNoSpeaking 查询全局禁言
|
||||||
|
// 查询帐号的单聊消息全局禁言。
|
||||||
|
// 查询帐号的群组消息全局禁言。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/4229
|
||||||
|
GetNoSpeaking(userId string) (ret *GetNoSpeakingRet, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type api struct {
|
||||||
|
client core.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAPI(client core.Client) API {
|
||||||
|
return &api{client: client}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNoSpeaking 设置全局禁言
|
||||||
|
// 设置帐号的单聊消息全局禁言。
|
||||||
|
// 设置帐号的群组消息全局禁言。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/4230
|
||||||
|
func (a *api) SetNoSpeaking(userId string, privateMuteTime, groupMuteTime *uint) (err error) {
|
||||||
|
req := &setNoSpeakingReq{
|
||||||
|
UserId: userId,
|
||||||
|
PrivateMuteTime: privateMuteTime,
|
||||||
|
GroupMuteTime: groupMuteTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandSetNoSpeaking, req, &types.BaseResp{}); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNoSpeaking 查询全局禁言
|
||||||
|
// 查询帐号的单聊消息全局禁言。
|
||||||
|
// 查询帐号的群组消息全局禁言。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/4229
|
||||||
|
func (a *api) GetNoSpeaking(userId string) (ret *GetNoSpeakingRet, err error) {
|
||||||
|
req := &getNoSpeakingReq{UserId: userId}
|
||||||
|
resp := &getNoSpeakingResp{}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandGetNoSpeaking, req, resp); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = &GetNoSpeakingRet{
|
||||||
|
PrivateMuteTime: resp.PrivateMuteTime,
|
||||||
|
GroupMuteTime: resp.GroupMuteTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
37
mute/types.go
Normal file
37
mute/types.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Email:1711788888@qq.com
|
||||||
|
* @Date: 2021/8/30 2:41 上午
|
||||||
|
* @Desc: 全局禁言数据类型
|
||||||
|
*/
|
||||||
|
|
||||||
|
package mute
|
||||||
|
|
||||||
|
import "git.echol.cn/loser/tencent-im/internal/types"
|
||||||
|
|
||||||
|
type (
|
||||||
|
// 设置全局禁言(请求)
|
||||||
|
setNoSpeakingReq struct {
|
||||||
|
UserId string `json:"Set_Account"` // (必填)设置禁言配置的帐号
|
||||||
|
PrivateMuteTime *uint `json:"C2CmsgNospeakingTime,omitempty"` // (选填)单聊消息禁言时间,单位为秒,非负整数,最大值为4294967295(十六进制 0xFFFFFFFF) 0表示取消该帐号的单聊消息禁言;4294967295表示该帐号被设置永久禁言;其它值表示该帐号具体的禁言时间
|
||||||
|
GroupMuteTime *uint `json:"GroupmsgNospeakingTime,omitempty"` // (选填)单聊消息禁言时间,单位为秒,非负整数,最大值为4294967295(十六进制 0xFFFFFFFF) 0表示取消该帐号的单聊消息禁言;4294967295表示该帐号被设置永久禁言;其它值表示该帐号具体的禁言时间
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置全局禁言(请求)
|
||||||
|
getNoSpeakingReq struct {
|
||||||
|
UserId string `json:"Get_Account"` // (必填)查询禁言信息的帐号
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置全局禁言(响应)
|
||||||
|
getNoSpeakingResp struct {
|
||||||
|
types.BaseResp
|
||||||
|
PrivateMuteTime uint `json:"C2CmsgNospeakingTime"` // 单聊消息禁言时长,单位为秒,非负整数。等于 0 代表没有被设置禁言;等于最大值4294967295(十六进制 0xFFFFFFFF)代表被设置永久禁言;其它代表该帐号禁言时长,如果等于3600表示该帐号被禁言一小时
|
||||||
|
GroupMuteTime uint `json:"GroupmsgNospeakingTime"` // 群组消息禁言时长,单位为秒,非负整数。等于0代表没有被设置禁言;等于最大值4294967295(十六进制 0xFFFFFFFF)代表被设置永久禁言;其它代表该帐号禁言时长,如果等于3600表示该帐号被禁言一小时
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNoSpeakingRet 获取全局禁言(返回)
|
||||||
|
GetNoSpeakingRet struct {
|
||||||
|
PrivateMuteTime uint // 单聊消息禁言时长,单位为秒,非负整数。等于 0 代表没有被设置禁言;等于最大值4294967295(十六进制 0xFFFFFFFF)代表被设置永久禁言;其它代表该帐号禁言时长,如果等于3600表示该帐号被禁言一小时
|
||||||
|
GroupMuteTime uint // 群组消息禁言时长,单位为秒,非负整数。等于0代表没有被设置禁言;等于最大值4294967295(十六进制 0xFFFFFFFF)代表被设置永久禁言;其它代表该帐号禁言时长,如果等于3600表示该帐号被禁言一小时
|
||||||
|
}
|
||||||
|
)
|
104
operation/api.go
Normal file
104
operation/api.go
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Author:1711788888@qq.com
|
||||||
|
* @Date: 2021/5/29 18:38
|
||||||
|
* @Desc: 运营管理
|
||||||
|
*/
|
||||||
|
|
||||||
|
package operation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
serviceOperation = "openconfigsvr"
|
||||||
|
serviceOpenMessage = "open_msg_svc"
|
||||||
|
serviceConfig = "ConfigSvc"
|
||||||
|
commandGetAppInfo = "getappinfo"
|
||||||
|
commandGetHistory = "get_history"
|
||||||
|
commandGetIPList = "GetIPList"
|
||||||
|
)
|
||||||
|
|
||||||
|
type API interface {
|
||||||
|
// GetOperationData 拉取运营数据
|
||||||
|
// App 管理员可以通过该接口拉取最近30天的运营数据,可拉取的字段见下文可拉取的运营字段。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/4193
|
||||||
|
GetOperationData(fields ...FieldType) (data []*OperationData, err error)
|
||||||
|
|
||||||
|
// GetHistoryData 下载最近消息记录
|
||||||
|
// App 管理员可以通过该接口获取 App 中最近7天中某天某小时的所有单发或群组消息记录的下载地址
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/1650
|
||||||
|
GetHistoryData(chatType ChatType, msgTime time.Time) (files []*HistoryFile, err error)
|
||||||
|
|
||||||
|
// GetIPList 获取服务器IP地址
|
||||||
|
// 基于安全等考虑,您可能需要获知服务器的 IP 地址列表,以便进行相关限制。
|
||||||
|
// App 管理员可以通过该接口获得 SDK、第三方回调所使用到的服务器 IP 地址列表或 IP 网段信息。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/45438
|
||||||
|
GetIPList() (ips []string, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type api struct {
|
||||||
|
client core.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAPI(client core.Client) API {
|
||||||
|
return &api{client: client}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOperationData 拉取运营数据
|
||||||
|
// App 管理员可以通过该接口拉取最近30天的运营数据,可拉取的字段见下文可拉取的运营字段。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/4193
|
||||||
|
func (a *api) GetOperationData(fields ...FieldType) (data []*OperationData, err error) {
|
||||||
|
req := &getOperationDataReq{Fields: fields}
|
||||||
|
resp := &getOperationDataResp{}
|
||||||
|
|
||||||
|
if err = a.client.Post(serviceOperation, commandGetAppInfo, req, resp); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data = resp.Data
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHistoryData 下载最近消息记录
|
||||||
|
// App 管理员可以通过该接口获取 App 中最近7天中某天某小时的所有单发或群组消息记录的下载地址
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/1650
|
||||||
|
func (a *api) GetHistoryData(chatType ChatType, msgTime time.Time) (files []*HistoryFile, err error) {
|
||||||
|
req := &getHistoryDataReq{ChatType: chatType, MsgTime: msgTime.Format("2006010215")}
|
||||||
|
resp := &getHistoryDataResp{}
|
||||||
|
|
||||||
|
if err = a.client.Post(serviceOpenMessage, commandGetHistory, req, resp); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
files = resp.Files
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIPList 获取服务器IP地址
|
||||||
|
// 基于安全等考虑,您可能需要获知服务器的 IP 地址列表,以便进行相关限制。
|
||||||
|
// App 管理员可以通过该接口获得 SDK、第三方回调所使用到的服务器 IP 地址列表或 IP 网段信息。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/45438
|
||||||
|
func (a *api) GetIPList() (ips []string, err error) {
|
||||||
|
req := &getIPListReq{}
|
||||||
|
resp := &getIPListResp{}
|
||||||
|
|
||||||
|
if err = a.client.Post(serviceConfig, commandGetIPList, req, resp); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ips = resp.IPList
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
51
operation/enum.go
Normal file
51
operation/enum.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Author:1711788888@qq.com
|
||||||
|
* @Date: 2021/9/7 10:28
|
||||||
|
* @Desc: 运营管理枚举参数
|
||||||
|
*/
|
||||||
|
|
||||||
|
package operation
|
||||||
|
|
||||||
|
type (
|
||||||
|
// ChatType 聊天类型
|
||||||
|
ChatType string
|
||||||
|
|
||||||
|
// FieldType 运营数据字段类型
|
||||||
|
FieldType string
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ChatTypeC2C ChatType = "C2C" // 单聊消息
|
||||||
|
ChatTypeGroup ChatType = "Group" // 群聊消息
|
||||||
|
|
||||||
|
FieldTypeAppName FieldType = "AppName" // 应用名称
|
||||||
|
FieldTypeAppId FieldType = "AppId" // 应用 SDKAppID
|
||||||
|
FieldTypeCompany FieldType = "Company" // 所属客户名称
|
||||||
|
FieldTypeActiveUserNum FieldType = "ActiveUserNum" // 活跃用户数
|
||||||
|
FieldTypeRegisterUserNumOneDay FieldType = "RegistUserNumOneDay" // 新增注册人数
|
||||||
|
FieldTypeRegisterUserNumTotal FieldType = "RegistUserNumTotal" // 累计注册人数
|
||||||
|
FieldTypeLoginTimes FieldType = "LoginTimes" // 登录次数
|
||||||
|
FieldTypeLoginUserNum FieldType = "LoginUserNum" // 登录人数
|
||||||
|
FieldTypeUpMsgNum FieldType = "UpMsgNum" // 上行消息数
|
||||||
|
FieldTypeSendMsgUserNum FieldType = "SendMsgUserNum" // 发消息人数
|
||||||
|
FieldTypeAPNSMsgNum FieldType = "APNSMsgNum" // APNs 推送数
|
||||||
|
FieldTypeC2CUpMsgNum FieldType = "C2CUpMsgNum" // 上行消息数(C2C)
|
||||||
|
FieldTypeC2CSendMsgUserNum FieldType = "C2CSendMsgUserNum" // 发消息人数(C2C)
|
||||||
|
FieldTypeC2CAPNSMsgNum FieldType = "C2CAPNSMsgNum" // APNs 推送数(C2C)
|
||||||
|
FieldTypeMaxOnlineNum FieldType = "MaxOnlineNum" // 最高在线人数
|
||||||
|
FieldTypeChainIncrease FieldType = "ChainIncrease" // 关系链对数增加量
|
||||||
|
FieldTypeChainDecrease FieldType = "ChainDecrease" // 关系链对数删除量
|
||||||
|
FieldTypeGroupUpMsgNum FieldType = "GroupUpMsgNum" // 上行消息数(群)
|
||||||
|
FieldTypeGroupSendMsgUserNum FieldType = "GroupSendMsgUserNum" // 发消息人数(群)
|
||||||
|
FieldTypeGroupAPNSMsgNum FieldType = "GroupAPNSMsgNum" // APNs 推送数(群)
|
||||||
|
FieldTypeGroupSendMsgGroupNum FieldType = "GroupSendMsgGroupNum" // 发消息群组数
|
||||||
|
FieldTypeGroupJoinGroupTimes FieldType = "GroupJoinGroupTimes" // 入群总数
|
||||||
|
FieldTypeGroupQuitGroupTimes FieldType = "GroupQuitGroupTimes" // 退群总数
|
||||||
|
FieldTypeGroupNewGroupNum FieldType = "GroupNewGroupNum" // 新增群组数
|
||||||
|
FieldTypeGroupAllGroupNum FieldType = "GroupAllGroupNum" // 累计群组数
|
||||||
|
FieldTypeGroupDestroyGroupNum FieldType = "GroupDestroyGroupNum" // 解散群个数
|
||||||
|
FieldTypeCallBackReq FieldType = "CallBackReq" // 回调请求数
|
||||||
|
FieldTypeCallBackRsp FieldType = "CallBackRsp" // 回调应答数
|
||||||
|
FieldTypeDate FieldType = "Date" // 日期
|
||||||
|
)
|
91
operation/types.go
Normal file
91
operation/types.go
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Author:1711788888@qq.com
|
||||||
|
* @Date: 2021/5/29 18:39
|
||||||
|
* @Desc: 运营管理数据类型
|
||||||
|
*/
|
||||||
|
|
||||||
|
package operation
|
||||||
|
|
||||||
|
import "git.echol.cn/loser/tencent-im/internal/types"
|
||||||
|
|
||||||
|
type (
|
||||||
|
// 拉取运营数据(请求)
|
||||||
|
getOperationDataReq struct {
|
||||||
|
Fields []FieldType `json:"RequestField,omitempty"` // 该字段用来指定需要拉取的运营数据,不填默认拉取所有字段。
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拉取运营数据(响应)
|
||||||
|
getOperationDataResp struct {
|
||||||
|
types.BaseResp
|
||||||
|
Data []*OperationData `json:"Result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OperationData 运营数据
|
||||||
|
OperationData struct {
|
||||||
|
AppId string `json:"AppId"` // 应用AppID
|
||||||
|
AppName string `json:"AppName"` // 应用名称
|
||||||
|
Company string `json:"Company"` // 所属客户名称
|
||||||
|
ActiveUserNum string `json:"ActiveUserNum"` // 活跃用户数
|
||||||
|
RegistUserNumOneDay string `json:"RegistUserNumOneDay"` // 新增注册人数
|
||||||
|
RegistUserNumTotal string `json:"RegistUserNumTotal"` // 累计注册人数
|
||||||
|
LoginTimes string `json:"LoginTimes"` // 登录次数
|
||||||
|
LoginUserNum string `json:"LoginUserNum"` // 登录人数
|
||||||
|
UpMsgNum string `json:"UpMsgNum"` // 上行消息数
|
||||||
|
DownMsgNum string `json:"DownMsgNum"` // 下行消息数
|
||||||
|
SendMsgUserNum string `json:"SendMsgUserNum"` // 发消息人数
|
||||||
|
APNSMsgNum string `json:"APNSMsgNum"` // APNs推送数
|
||||||
|
C2CUpMsgNum string `json:"C2CUpMsgNum"` // 上行消息数(C2C)
|
||||||
|
C2CSendMsgUserNum string `json:"C2CSendMsgUserNum"` // 发消息人数(C2C)
|
||||||
|
C2CAPNSMsgNum string `json:"C2CAPNSMsgNum"` // APNs推送数(C2C)
|
||||||
|
C2CDownMsgNum string `json:"C2CDownMsgNum"` // 下行消息数(C2C)
|
||||||
|
MaxOnlineNum string `json:"MaxOnlineNum"` // 最高在线人数
|
||||||
|
ChainDecrease string `json:"ChainDecrease"` // 关系链对数删除量
|
||||||
|
ChainIncrease string `json:"ChainIncrease"` // 关系链对数增加量
|
||||||
|
GroupUpMsgNum string `json:"GroupUpMsgNum"` // 上行消息数(群)
|
||||||
|
GroupDownMsgNum string `json:"GroupDownMsgNum"` // 下行消息数(群)
|
||||||
|
GroupSendMsgUserNum string `json:"GroupSendMsgUserNum"` // 发消息人数(群)
|
||||||
|
GroupAPNSMsgNum string `json:"GroupAPNSMsgNum"` // APNs推送数(群)
|
||||||
|
GroupSendMsgGroupNum string `json:"GroupSendMsgGroupNum"` // 发消息群组数
|
||||||
|
GroupJoinGroupTimes string `json:"GroupJoinGroupTimes"` // 入群总数
|
||||||
|
GroupQuitGroupTimes string `json:"GroupQuitGroupTimes"` // 退群总数
|
||||||
|
GroupNewGroupNum string `json:"GroupNewGroupNum"` // 新增群组数
|
||||||
|
GroupAllGroupNum string `json:"GroupAllGroupNum"` // 累计群组数
|
||||||
|
GroupDestroyGroupNum string `json:"GroupDestroyGroupNum"` // 解散群个数
|
||||||
|
CallBackReq string `json:"CallBackReq"` // 回调请求数
|
||||||
|
CallBackRsp string `json:"CallBackRsp"` // 回调应答数
|
||||||
|
Date string `json:"Date"` // 日期
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取历史数据(请求)
|
||||||
|
getHistoryDataReq struct {
|
||||||
|
ChatType ChatType `json:"ChatType"` // (必填)消息类型,C2C 表示单发消息 Group 表示群组消息
|
||||||
|
MsgTime string `json:"MsgTime"` // (必填)需要下载的消息记录的时间段,2015120121表示获取2015年12月1日21:00 - 21:59的消息的下载地址。该字段需精确到小时。每次请求只能获取某天某小时的所有单发或群组消息记录
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取历史数据(响应)
|
||||||
|
getHistoryDataResp struct {
|
||||||
|
types.BaseResp
|
||||||
|
Files []*HistoryFile `json:"File"` // 消息记录文件下载信息
|
||||||
|
}
|
||||||
|
|
||||||
|
// HistoryFile 历史数据文件
|
||||||
|
HistoryFile struct {
|
||||||
|
URL string `json:"URL"` // 消息记录文件下载地址
|
||||||
|
ExpireTime string `json:"ExpireTime"` // 下载地址过期时间,请在过期前进行下载,若地址失效,请通过该接口重新获取
|
||||||
|
FileSize int `json:"FileSize"` // GZip 压缩前的文件大小(单位 Byte)
|
||||||
|
FileMD5 string `json:"FileMD5"` // GZip 压缩前的文件 MD5
|
||||||
|
GzipSize int `json:"GzipSize"` // GZip 压缩后的文件大小(单位 Byte)
|
||||||
|
GzipMD5 string `json:"GzipMD5"` // GZip 压缩后的文件 MD5
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取服务器IP地址(请求)
|
||||||
|
getIPListReq struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取服务器IP地址(响应)
|
||||||
|
getIPListResp struct {
|
||||||
|
types.BaseResp
|
||||||
|
IPList []string `json:"IPList"` // 服务器IP列表
|
||||||
|
}
|
||||||
|
)
|
354
private/api.go
Normal file
354
private/api.go
Normal file
@ -0,0 +1,354 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Author:1711788888@qq.com
|
||||||
|
* @Date: 2021/5/27 19:32
|
||||||
|
* @Desc: 单聊消息
|
||||||
|
*/
|
||||||
|
|
||||||
|
package private
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/conv"
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/core"
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
service = "openim"
|
||||||
|
commandSendMessage = "sendmsg"
|
||||||
|
commandSendMessages = "batchsendmsg"
|
||||||
|
commandImportMessage = "importmsg"
|
||||||
|
commandFetchMessages = "admin_getroammsg"
|
||||||
|
commandRevokeMessage = "admin_msgwithdraw"
|
||||||
|
commandSetMessageRead = "admin_set_msg_read"
|
||||||
|
commandGetUnreadMessageNum = "get_c2c_unread_msg_num"
|
||||||
|
)
|
||||||
|
|
||||||
|
type API interface {
|
||||||
|
// SendMessage 单发单聊消息
|
||||||
|
// 管理员向帐号发消息,接收方看到消息发送者是管理员。
|
||||||
|
// 管理员指定某一帐号向其他帐号发消息,接收方看到发送者不是管理员,而是管理员指定的帐号。
|
||||||
|
// 该接口不会检查发送者和接收者的好友关系(包括黑名单),同时不会检查接收者是否被禁言。
|
||||||
|
// 单聊消息 MsgSeq 字段的作用及说明:该字段在发送消息时由用户自行指定,该值可以重复,非后台生成,非全局唯一。与群聊消息的 MsgSeq 字段不同,群聊消息的 MsgSeq 由后台生成,每个群都维护一个 MsgSeq,从1开始严格递增。单聊消息历史记录对同一个会话的消息先以时间戳排序,同秒内的消息再以 MsgSeq 排序。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/2282
|
||||||
|
SendMessage(message *Message) (ret *SendMessageRet, err error)
|
||||||
|
|
||||||
|
// SendMessages 批量发单聊消息
|
||||||
|
// 支持一次对最多500个用户进行单发消息。
|
||||||
|
// 与单发消息相比,该接口更适用于营销类消息、系统通知 tips 等时效性较强的消息。
|
||||||
|
// 管理员指定某一帐号向目标帐号批量发消息,接收方看到发送者不是管理员,而是管理员指定的帐号。
|
||||||
|
// 该接口不触发回调请求。
|
||||||
|
// 该接口不会检查发送者和接收者的好友关系(包括黑名单),同时不会检查接收者是否被禁言。
|
||||||
|
// 单聊消息 MsgSeq 字段的作用及说明:该字段在发送消息时由用户自行指定,该值可以重复,非后台生成,非全局唯一。与群聊消息的 MsgSeq 字段不同,群聊消息的 MsgSeq 由后台生成,每个群都维护一个 MsgSeq,从1开始严格递增。单聊消息历史记录对同一个会话的消息先以时间戳排序,同秒内的消息再以 MsgSeq 排序。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/1612
|
||||||
|
SendMessages(message *Message) (ret *SendMessagesRet, err error)
|
||||||
|
|
||||||
|
// ImportMessage 导入单聊消息
|
||||||
|
// 导入历史单聊消息到即时通信 IM。
|
||||||
|
// 平滑过渡期间,将原有即时通信实时单聊消息导入到即时通信 IM。
|
||||||
|
// 该接口不会触发回调。
|
||||||
|
// 该接口会根据 From_Account , To_Account ,MsgSeq , MsgRandom , MsgTimeStamp 字段的值对导入的消息进行去重。仅当这五个字段的值都对应相同时,才判定消息是重复的,消息是否重复与消息内容本身无关。
|
||||||
|
// 重复导入的消息不会覆盖之前已导入的消息(即消息内容以首次导入的为准)。
|
||||||
|
// 单聊消息 MsgSeq 字段的作用及说明:该字段在发送消息时由用户自行指定,该值可以重复,非后台生成,非全局唯一。与群聊消息的 MsgSeq 字段不同,群聊消息的 MsgSeq 由后台生成,每个群都维护一个 MsgSeq,从1开始严格递增。单聊消息历史记录对同一个会话的消息先以时间戳排序,同秒内的消息再以 MsgSeq 排序。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/2568
|
||||||
|
ImportMessage(message *Message) (err error)
|
||||||
|
|
||||||
|
// FetchMessages 查询单聊消息
|
||||||
|
// 管理员按照时间范围查询某单聊会话的消息记录。
|
||||||
|
// 查询的单聊会话由请求中的 From_Account 和 To_Account 指定。查询结果包含会话双方互相发送的消息,具体每条消息的发送方和接收方由每条消息里的 From_Account 和 To_Account 指定。
|
||||||
|
// 一般情况下,请求中的 From_Account 和 To_Account 字段值互换,查询结果不变。但通过 单发单聊消息 或 批量发单聊消息 接口发送的消息,如果指定 SyncOtherMachine 值为2,则需要指定正确的 From_Account 和 To_Account 字段值才能查询到该消息。
|
||||||
|
// 例如,通过 单发单聊消息 接口指定帐号 A 给帐号 B 发一条消息,同时指定 SyncOtherMachine 值为2。则调用本接口时,From_Account 必须设置为帐号 B,To_Account 必须设置为帐号 A 才能查询到该消息。
|
||||||
|
// 查询结果包含被撤回的消息,由消息里的 MsgFlagBits 字段标识。
|
||||||
|
// 若想通过 REST API 撤回单聊消息 接口撤回某条消息,可先用本接口查询出该消息的 MsgKey,然后再调用撤回接口进行撤回。
|
||||||
|
// 可查询的消息记录的时间范围取决于漫游消息存储时长,默认是7天。支持在控制台修改消息漫游时长,延长消息漫游时长是增值服务。具体请参考 漫游消息存储。
|
||||||
|
// 若请求时间段内的消息总大小超过应答包体大小限制(目前为13K)时,则需要续拉。您可以通过应答中的 Complete 字段查看是否已拉取请求的全部消息。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/42794
|
||||||
|
FetchMessages(arg *FetchMessagesArg) (ret *FetchMessagesRet, err error)
|
||||||
|
|
||||||
|
// PullMessages 续拉取单聊消息
|
||||||
|
// 本API是借助"查询单聊消息"API进行扩展实现
|
||||||
|
// 管理员按照时间范围查询某单聊会话的全部消息记录
|
||||||
|
// 查询的单聊会话由请求中的 From_Account 和 To_Account 指定。查询结果包含会话双方互相发送的消息,具体每条消息的发送方和接收方由每条消息里的 From_Account 和 To_Account 指定。
|
||||||
|
// 一般情况下,请求中的 From_Account 和 To_Account 字段值互换,查询结果不变。但通过 单发单聊消息 或 批量发单聊消息 接口发送的消息,如果指定 SyncOtherMachine 值为2,则需要指定正确的 From_Account 和 To_Account 字段值才能查询到该消息。
|
||||||
|
// 例如,通过 单发单聊消息 接口指定帐号 A 给帐号 B 发一条消息,同时指定 SyncOtherMachine 值为2。则调用本接口时,From_Account 必须设置为帐号 B,To_Account 必须设置为帐号 A 才能查询到该消息。
|
||||||
|
// 查询结果包含被撤回的消息,由消息里的 MsgFlagBits 字段标识。
|
||||||
|
// 若想通过 REST API 撤回单聊消息 接口撤回某条消息,可先用本接口查询出该消息的 MsgKey,然后再调用撤回接口进行撤回。
|
||||||
|
// 可查询的消息记录的时间范围取决于漫游消息存储时长,默认是7天。支持在控制台修改消息漫游时长,延长消息漫游时长是增值服务。具体请参考 漫游消息存储。
|
||||||
|
// 若请求时间段内的消息总大小超过应答包体大小限制(目前为13K)时,则需要续拉。您可以通过应答中的 Complete 字段查看是否已拉取请求的全部消息。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/42794
|
||||||
|
PullMessages(arg *PullMessagesArg, fn func(ret *FetchMessagesRet)) (err error)
|
||||||
|
|
||||||
|
// RevokeMessage 撤回单聊消息
|
||||||
|
// 管理员撤回单聊消息。
|
||||||
|
// 该接口可以撤回所有单聊消息,包括客户端发出的单聊消息,由 REST API 单发 和 批量发 接口发出的单聊消息。
|
||||||
|
// 若需要撤回由客户端发出的单聊消息,您可以开通 发单聊消息之前回调 或 发单聊消息之后回调 ,通过该回调接口记录每条单聊消息的 MsgKey ,然后填在本接口的 MsgKey 字段进行撤回。您也可以通过 查询单聊消息 查询出待撤回的单聊消息的 MsgKey 后,填在本接口的 MsgKey 字段进行撤回。
|
||||||
|
// 若需要撤回由 REST API 单发 和 批量发 接口发出的单聊消息,需要记录这些接口回包里的 MsgKey 字段以进行撤回。
|
||||||
|
// 调用该接口撤回消息后,该条消息的离线、漫游存储,以及消息发送方和接收方的客户端的本地缓存都会被撤回。
|
||||||
|
// 该接口可撤回的单聊消息没有时间限制,即可以撤回任何时间的单聊消息。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/38980
|
||||||
|
RevokeMessage(fromUserId, toUserId, msgKey string) (err error)
|
||||||
|
|
||||||
|
// SetMessageRead 设置单聊消息已读
|
||||||
|
// 设置用户的某个单聊会话的消息全部已读。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/50349
|
||||||
|
SetMessageRead(userId, peerUserId string) (err error)
|
||||||
|
|
||||||
|
// GetUnreadMessageNum 查询单聊未读消息计数
|
||||||
|
// App 后台可以通过该接口查询特定账号的单聊总未读数(包含所有的单聊会话)或者单个单聊会话的未读数。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/56043
|
||||||
|
GetUnreadMessageNum(userId string, peerUserIds ...string) (ret *GetUnreadMessageNumRet, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type api struct {
|
||||||
|
client core.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAPI(client core.Client) API {
|
||||||
|
return &api{client: client}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendMessage 单发单聊消息
|
||||||
|
// 管理员向帐号发消息,接收方看到消息发送者是管理员。
|
||||||
|
// 管理员指定某一帐号向其他帐号发消息,接收方看到发送者不是管理员,而是管理员指定的帐号。
|
||||||
|
// 该接口不会检查发送者和接收者的好友关系(包括黑名单),同时不会检查接收者是否被禁言。
|
||||||
|
// 单聊消息 MsgSeq 字段的作用及说明:该字段在发送消息时由用户自行指定,该值可以重复,非后台生成,非全局唯一。与群聊消息的 MsgSeq 字段不同,群聊消息的 MsgSeq 由后台生成,每个群都维护一个 MsgSeq,从1开始严格递增。单聊消息历史记录对同一个会话的消息先以时间戳排序,同秒内的消息再以 MsgSeq 排序。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/2282
|
||||||
|
func (a *api) SendMessage(message *Message) (ret *SendMessageRet, err error) {
|
||||||
|
if err = message.CheckError(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &sendMessageReq{}
|
||||||
|
req.FromUserId = message.GetSender()
|
||||||
|
req.ToUserId = message.GetLastReceiver()
|
||||||
|
req.MsgLifeTime = message.GetLifeTime()
|
||||||
|
req.MsgTimeStamp = message.GetTimestamp()
|
||||||
|
req.OfflinePushInfo = message.GetOfflinePushInfo()
|
||||||
|
req.CloudCustomData = conv.String(message.GetCustomData())
|
||||||
|
req.MsgSeq = message.GetSerialNo()
|
||||||
|
req.MsgBody = message.GetBody()
|
||||||
|
req.MsgRandom = message.GetRandom()
|
||||||
|
req.SendMsgControl = message.GetSendMsgControl()
|
||||||
|
req.ForbidCallbackControl = message.GetForbidCallbackControl()
|
||||||
|
req.SyncOtherMachine = message.GetSyncOtherMachine()
|
||||||
|
|
||||||
|
resp := &sendMessageResp{}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandSendMessage, req, resp); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = &SendMessageRet{
|
||||||
|
MsgKey: resp.MsgKey,
|
||||||
|
MsgTime: resp.MsgTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendMessages 批量发单聊消息
|
||||||
|
// 支持一次对最多500个用户进行单发消息。
|
||||||
|
// 与单发消息相比,该接口更适用于营销类消息、系统通知 tips 等时效性较强的消息。
|
||||||
|
// 管理员指定某一帐号向目标帐号批量发消息,接收方看到发送者不是管理员,而是管理员指定的帐号。
|
||||||
|
// 该接口不触发回调请求。
|
||||||
|
// 该接口不会检查发送者和接收者的好友关系(包括黑名单),同时不会检查接收者是否被禁言。
|
||||||
|
// 单聊消息 MsgSeq 字段的作用及说明:该字段在发送消息时由用户自行指定,该值可以重复,非后台生成,非全局唯一。与群聊消息的 MsgSeq 字段不同,群聊消息的 MsgSeq 由后台生成,每个群都维护一个 MsgSeq,从1开始严格递增。单聊消息历史记录对同一个会话的消息先以时间戳排序,同秒内的消息再以 MsgSeq 排序。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/1612
|
||||||
|
func (a *api) SendMessages(message *Message) (ret *SendMessagesRet, err error) {
|
||||||
|
if err = message.CheckError(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &sendMessagesReq{}
|
||||||
|
req.FromUserId = message.GetSender()
|
||||||
|
req.ToUserIds = message.GetReceivers()
|
||||||
|
req.OfflinePushInfo = message.GetOfflinePushInfo()
|
||||||
|
req.CloudCustomData = conv.String(message.GetCustomData())
|
||||||
|
req.MsgSeq = message.GetSerialNo()
|
||||||
|
req.MsgBody = message.GetBody()
|
||||||
|
req.MsgRandom = message.GetRandom()
|
||||||
|
req.SendMsgControl = message.GetSendMsgControl()
|
||||||
|
req.SyncOtherMachine = message.GetSyncOtherMachine()
|
||||||
|
|
||||||
|
resp := &sendMessagesResp{}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandSendMessages, req, resp); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = &SendMessagesRet{
|
||||||
|
MsgKey: resp.MsgKey,
|
||||||
|
Errors: resp.Errors,
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImportMessage 导入单聊消息
|
||||||
|
// 导入历史单聊消息到即时通信 IM。
|
||||||
|
// 平滑过渡期间,将原有即时通信实时单聊消息导入到即时通信 IM。
|
||||||
|
// 该接口不会触发回调。
|
||||||
|
// 该接口会根据 From_Account , To_Account ,MsgSeq , MsgRandom , MsgTimeStamp 字段的值对导入的消息进行去重。仅当这五个字段的值都对应相同时,才判定消息是重复的,消息是否重复与消息内容本身无关。
|
||||||
|
// 重复导入的消息不会覆盖之前已导入的消息(即消息内容以首次导入的为准)。
|
||||||
|
// 单聊消息 MsgSeq 字段的作用及说明:该字段在发送消息时由用户自行指定,该值可以重复,非后台生成,非全局唯一。与群聊消息的 MsgSeq 字段不同,群聊消息的 MsgSeq 由后台生成,每个群都维护一个 MsgSeq,从1开始严格递增。单聊消息历史记录对同一个会话的消息先以时间戳排序,同秒内的消息再以 MsgSeq 排序。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/2568
|
||||||
|
func (a *api) ImportMessage(message *Message) (err error) {
|
||||||
|
if err = message.CheckError(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &importMessageReq{}
|
||||||
|
req.FromUserId = message.GetSender()
|
||||||
|
req.ToUserId = message.GetLastReceiver()
|
||||||
|
req.MsgTimeStamp = message.GetTimestamp()
|
||||||
|
req.CloudCustomData = conv.String(message.GetCustomData())
|
||||||
|
req.MsgSeq = message.GetSerialNo()
|
||||||
|
req.MsgBody = message.GetBody()
|
||||||
|
req.MsgRandom = message.GetRandom()
|
||||||
|
req.SyncFromOldSystem = message.GetSyncOtherMachine()
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandImportMessage, req, &types.ActionBaseResp{}); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FetchMessages 查询单聊消息
|
||||||
|
// 管理员按照时间范围查询某单聊会话的消息记录。
|
||||||
|
// 查询的单聊会话由请求中的 From_Account 和 To_Account 指定。查询结果包含会话双方互相发送的消息,具体每条消息的发送方和接收方由每条消息里的 From_Account 和 To_Account 指定。
|
||||||
|
// 一般情况下,请求中的 From_Account 和 To_Account 字段值互换,查询结果不变。但通过 单发单聊消息 或 批量发单聊消息 接口发送的消息,如果指定 SyncOtherMachine 值为2,则需要指定正确的 From_Account 和 To_Account 字段值才能查询到该消息。
|
||||||
|
// 例如,通过 单发单聊消息 接口指定帐号 A 给帐号 B 发一条消息,同时指定 SyncOtherMachine 值为2。则调用本接口时,From_Account 必须设置为帐号 B,To_Account 必须设置为帐号 A 才能查询到该消息。
|
||||||
|
// 查询结果包含被撤回的消息,由消息里的 MsgFlagBits 字段标识。
|
||||||
|
// 若想通过 REST API 撤回单聊消息 接口撤回某条消息,可先用本接口查询出该消息的 MsgKey,然后再调用撤回接口进行撤回。
|
||||||
|
// 可查询的消息记录的时间范围取决于漫游消息存储时长,默认是7天。支持在控制台修改消息漫游时长,延长消息漫游时长是增值服务。具体请参考 漫游消息存储。
|
||||||
|
// 若请求时间段内的消息总大小超过应答包体大小限制(目前为13K)时,则需要续拉。您可以通过应答中的 Complete 字段查看是否已拉取请求的全部消息。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/42794
|
||||||
|
func (a *api) FetchMessages(arg *FetchMessagesArg) (ret *FetchMessagesRet, err error) {
|
||||||
|
resp := &fetchMessagesResp{}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandFetchMessages, arg, resp); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = &FetchMessagesRet{
|
||||||
|
LastMsgKey: resp.LastMsgKey,
|
||||||
|
LastMsgTime: resp.LastMsgTime,
|
||||||
|
Count: resp.MsgCount,
|
||||||
|
List: resp.MsgList,
|
||||||
|
HasMore: resp.Complete != 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// PullMessages 续拉取单聊消息
|
||||||
|
// 本API是借助"查询单聊消息"API进行扩展实现
|
||||||
|
// 管理员按照时间范围查询某单聊会话的全部消息记录
|
||||||
|
// 查询的单聊会话由请求中的 From_Account 和 To_Account 指定。查询结果包含会话双方互相发送的消息,具体每条消息的发送方和接收方由每条消息里的 From_Account 和 To_Account 指定。
|
||||||
|
// 一般情况下,请求中的 From_Account 和 To_Account 字段值互换,查询结果不变。但通过 单发单聊消息 或 批量发单聊消息 接口发送的消息,如果指定 SyncOtherMachine 值为2,则需要指定正确的 From_Account 和 To_Account 字段值才能查询到该消息。
|
||||||
|
// 例如,通过 单发单聊消息 接口指定帐号 A 给帐号 B 发一条消息,同时指定 SyncOtherMachine 值为2。则调用本接口时,From_Account 必须设置为帐号 B,To_Account 必须设置为帐号 A 才能查询到该消息。
|
||||||
|
// 查询结果包含被撤回的消息,由消息里的 MsgFlagBits 字段标识。
|
||||||
|
// 若想通过 REST API 撤回单聊消息 接口撤回某条消息,可先用本接口查询出该消息的 MsgKey,然后再调用撤回接口进行撤回。
|
||||||
|
// 可查询的消息记录的时间范围取决于漫游消息存储时长,默认是7天。支持在控制台修改消息漫游时长,延长消息漫游时长是增值服务。具体请参考 漫游消息存储。
|
||||||
|
// 若请求时间段内的消息总大小超过应答包体大小限制(目前为13K)时,则需要续拉。您可以通过应答中的 Complete 字段查看是否已拉取请求的全部消息。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/42794
|
||||||
|
func (a *api) PullMessages(arg *PullMessagesArg, fn func(ret *FetchMessagesRet)) (err error) {
|
||||||
|
var (
|
||||||
|
ret *FetchMessagesRet
|
||||||
|
req = &FetchMessagesArg{
|
||||||
|
FromUserId: arg.FromUserId,
|
||||||
|
ToUserId: arg.ToUserId,
|
||||||
|
MaxLimited: arg.MaxLimited,
|
||||||
|
MinTime: arg.MinTime,
|
||||||
|
MaxTime: arg.MaxTime,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
for ret == nil || ret.HasMore {
|
||||||
|
ret, err = a.FetchMessages(req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fn(ret)
|
||||||
|
|
||||||
|
if ret.HasMore {
|
||||||
|
req.LastMsgKey = ret.LastMsgKey
|
||||||
|
req.MaxTime = ret.LastMsgTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// RevokeMessage 撤回单聊消息
|
||||||
|
// 管理员撤回单聊消息。
|
||||||
|
// 该接口可以撤回所有单聊消息,包括客户端发出的单聊消息,由 REST API 单发 和 批量发 接口发出的单聊消息。
|
||||||
|
// 若需要撤回由客户端发出的单聊消息,您可以开通 发单聊消息之前回调 或 发单聊消息之后回调 ,通过该回调接口记录每条单聊消息的 MsgKey ,然后填在本接口的 MsgKey 字段进行撤回。您也可以通过 查询单聊消息 查询出待撤回的单聊消息的 MsgKey 后,填在本接口的 MsgKey 字段进行撤回。
|
||||||
|
// 若需要撤回由 REST API 单发 和 批量发 接口发出的单聊消息,需要记录这些接口回包里的 MsgKey 字段以进行撤回。
|
||||||
|
// 调用该接口撤回消息后,该条消息的离线、漫游存储,以及消息发送方和接收方的客户端的本地缓存都会被撤回。
|
||||||
|
// 该接口可撤回的单聊消息没有时间限制,即可以撤回任何时间的单聊消息。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/38980
|
||||||
|
func (a *api) RevokeMessage(fromUserId, toUserId, msgKey string) (err error) {
|
||||||
|
req := &revokeMessageReq{FromUserId: fromUserId, ToUserId: toUserId, MsgKey: msgKey}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandRevokeMessage, req, &types.ActionBaseResp{}); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMessageRead 设置单聊消息已读
|
||||||
|
// 设置用户的某个单聊会话的消息全部已读。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/50349
|
||||||
|
func (a *api) SetMessageRead(userId, peerUserId string) (err error) {
|
||||||
|
req := &setMessageReadReq{UserId: userId, PeerUserId: peerUserId}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandSetMessageRead, req, &types.ActionBaseResp{}); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUnreadMessageNum 查询单聊未读消息计数
|
||||||
|
// App 后台可以通过该接口查询特定账号的单聊总未读数(包含所有的单聊会话)或者单个单聊会话的未读数。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/56043
|
||||||
|
func (a *api) GetUnreadMessageNum(userId string, peerUserIds ...string) (ret *GetUnreadMessageNumRet, err error) {
|
||||||
|
req := &getUnreadMessageNumReq{UserId: userId, PeerUserIds: peerUserIds}
|
||||||
|
resp := &getUnreadMessageNumResp{}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandGetUnreadMessageNum, req, resp); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = &GetUnreadMessageNumRet{
|
||||||
|
Total: resp.AllUnreadMsgNum,
|
||||||
|
Results: make(map[string]int, len(resp.PeerUnreadMsgNums)),
|
||||||
|
Errors: resp.PeerErrors,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range resp.PeerUnreadMsgNums {
|
||||||
|
ret.Results[item.UserId] = item.UnreadMsgNum
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
42
private/enum.go
Normal file
42
private/enum.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Author:1711788888@qq.com
|
||||||
|
* @Date: 2021/9/7 10:22
|
||||||
|
* @Desc: TODO
|
||||||
|
*/
|
||||||
|
|
||||||
|
package private
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// 同步至其他设备
|
||||||
|
SyncOtherMachineYes = enum.SyncOtherMachineYes // 把消息同步到From_Account在线终端和漫游上
|
||||||
|
SyncOtherMachineNo = enum.SyncOtherMachineNo // 消息不同步至From_Account
|
||||||
|
|
||||||
|
// 推送标识
|
||||||
|
PushFlagYes = enum.PushFlagYes // 正常推送
|
||||||
|
PushFlagNo = enum.PushFlagYes // 不离线推送
|
||||||
|
|
||||||
|
// 华为推送通知消息分类
|
||||||
|
HuaWeiImportanceLow = enum.HuaWeiImportanceLow // LOW类消息
|
||||||
|
HuaWeiImportanceNormal = enum.HuaWeiImportanceNormal // NORMAL类消息
|
||||||
|
|
||||||
|
// 华为推送为“打开应用内指定页面”的前提下透传参数行为
|
||||||
|
HuaweiIntentParamAction = enum.HuaweiIntentParamAction // 将透传内容Ext作为Action参数
|
||||||
|
HuaweiIntentParamIntent = enum.HuaweiIntentParamIntent // 将透传内容Ext作为Intent参数
|
||||||
|
|
||||||
|
// VIVO手机推送消息分类
|
||||||
|
VivoClassificationOperation = enum.VivoClassificationOperation // 运营类消息
|
||||||
|
VivoClassificationSystem = enum.VivoClassificationSystem // 系统类消息
|
||||||
|
|
||||||
|
// IOS徽章计数模式
|
||||||
|
BadgeModeNormal = enum.BadgeModeNormal // 本条消息需要计数
|
||||||
|
BadgeModeIgnore = enum.BadgeModeIgnore // 本条消息不需要计数
|
||||||
|
|
||||||
|
// IOS10的推送扩展开关
|
||||||
|
MutableContentNormal = enum.MutableContentNormal // 关闭iOS10的推送扩展
|
||||||
|
MutableContentEnable = enum.MutableContentEnable // 开启iOS10的推送扩展
|
||||||
|
)
|
183
private/message.go
Normal file
183
private/message.go
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Author:1711788888@qq.com
|
||||||
|
* @Date: 2021/8/31 18:04
|
||||||
|
* @Desc: 私聊消息实体
|
||||||
|
*/
|
||||||
|
|
||||||
|
package private
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/entity"
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errNotSetMsgReceiver = errors.New("message receiver is not set")
|
||||||
|
|
||||||
|
type Message struct {
|
||||||
|
entity.Message
|
||||||
|
receivers []string // 接收方UserId(可以为多个)
|
||||||
|
syncOtherMachine int // 同步到其他器
|
||||||
|
timestamp int64 // 消息时间戳,UNIX 时间戳(单位:秒)
|
||||||
|
seq int // 消息序列号
|
||||||
|
customData interface{} // 自定义数据
|
||||||
|
sendControls map[string]bool // 发送消息控制
|
||||||
|
callbackControls map[string]bool // 禁用回调
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMessage() *Message {
|
||||||
|
return &Message{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddReceivers 添加接收方
|
||||||
|
func (m *Message) AddReceivers(userId ...string) {
|
||||||
|
if m.receivers == nil {
|
||||||
|
m.receivers = make([]string, 0)
|
||||||
|
}
|
||||||
|
m.receivers = append(m.receivers, userId...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetReceivers 设置接收方
|
||||||
|
func (m *Message) SetReceivers(userId ...string) {
|
||||||
|
if m.receivers != nil {
|
||||||
|
m.receivers = m.receivers[0:0]
|
||||||
|
}
|
||||||
|
m.AddReceivers(userId...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetReceivers 获取接收方
|
||||||
|
func (m *Message) GetReceivers() []string {
|
||||||
|
return m.receivers
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Message) GetLastReceiver() string {
|
||||||
|
return m.receivers[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSyncOtherMachine 设置同步到其他机器
|
||||||
|
func (m *Message) SetSyncOtherMachine(syncOtherMachine types.SyncOtherMachine) {
|
||||||
|
m.syncOtherMachine = int(syncOtherMachine)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSyncOtherMachine 获取同步至其他设备
|
||||||
|
func (m *Message) GetSyncOtherMachine() int {
|
||||||
|
return m.syncOtherMachine
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSerialNo 设置消息序列号
|
||||||
|
func (m *Message) SetSerialNo(seq int) {
|
||||||
|
m.seq = seq
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSerialNo 获取消息序列号
|
||||||
|
func (m *Message) GetSerialNo() int {
|
||||||
|
return m.seq
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTimestamp 设置消息的时间戳
|
||||||
|
func (m *Message) SetTimestamp(timestamp int64) {
|
||||||
|
m.timestamp = timestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTimestamp 获取消息的时间戳
|
||||||
|
func (m *Message) GetTimestamp() int64 {
|
||||||
|
return m.timestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCustomData 设置自定义数据
|
||||||
|
func (m *Message) SetCustomData(data interface{}) {
|
||||||
|
m.customData = data
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCustomData 获取自定义数据
|
||||||
|
func (m *Message) GetCustomData() interface{} {
|
||||||
|
return m.customData
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetForbidBeforeSendMsgCallback 设置禁止发消息前回调
|
||||||
|
func (m *Message) SetForbidBeforeSendMsgCallback() {
|
||||||
|
if m.callbackControls == nil {
|
||||||
|
m.callbackControls = make(map[string]bool, 0)
|
||||||
|
}
|
||||||
|
m.callbackControls["ForbidBeforeSendMsgCallback"] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetForbidAfterSendMsgCallback 设置禁止发消息后回调
|
||||||
|
func (m *Message) SetForbidAfterSendMsgCallback() {
|
||||||
|
if m.callbackControls == nil {
|
||||||
|
m.callbackControls = make(map[string]bool, 0)
|
||||||
|
}
|
||||||
|
m.callbackControls["ForbidAfterSendMsgCallback"] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetForbidCallbackControl 获取消息回调禁止开关
|
||||||
|
func (m *Message) GetForbidCallbackControl() (controls []string) {
|
||||||
|
if m.callbackControls != nil {
|
||||||
|
if n := len(m.callbackControls); n > 0 {
|
||||||
|
controls = make([]string, 0, n)
|
||||||
|
for k := range m.callbackControls {
|
||||||
|
controls = append(controls, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNoUnread 设置该条消息不计入未读数
|
||||||
|
func (m *Message) SetNoUnread() {
|
||||||
|
if m.sendControls == nil {
|
||||||
|
m.sendControls = make(map[string]bool, 0)
|
||||||
|
}
|
||||||
|
m.sendControls["NoUnread"] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNoLastMsg 设置该条消息不更新会话列表
|
||||||
|
func (m *Message) SetNoLastMsg() {
|
||||||
|
if m.sendControls == nil {
|
||||||
|
m.sendControls = make(map[string]bool, 0)
|
||||||
|
}
|
||||||
|
m.sendControls["NoLastMsg"] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSendMsgControl 获取消息发送控制选项
|
||||||
|
func (m *Message) GetSendMsgControl() (controls []string) {
|
||||||
|
if m.sendControls != nil {
|
||||||
|
if n := len(m.sendControls); n > 0 {
|
||||||
|
controls = make([]string, 0, n)
|
||||||
|
for k := range m.sendControls {
|
||||||
|
controls = append(controls, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckError 检测错误
|
||||||
|
func (m *Message) CheckError() (err error) {
|
||||||
|
if err = m.CheckLifeTimeArgError(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = m.CheckBodyArgError(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = m.checkReceiverArgError(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkReceiverArgError 检测接收方参数
|
||||||
|
func (m *Message) checkReceiverArgError() error {
|
||||||
|
if m.receivers == nil || len(m.receivers) == 0 {
|
||||||
|
return errNotSetMsgReceiver
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
192
private/types.go
Normal file
192
private/types.go
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Author:1711788888@qq.com
|
||||||
|
* @Date: 2021/5/29 17:42
|
||||||
|
* @Desc: 私聊消息数据结构
|
||||||
|
*/
|
||||||
|
|
||||||
|
package private
|
||||||
|
|
||||||
|
import "git.echol.cn/loser/tencent-im/internal/types"
|
||||||
|
|
||||||
|
type (
|
||||||
|
// 发送消息(请求)
|
||||||
|
sendMessageReq struct {
|
||||||
|
FromUserId string `json:"From_Account,omitempty"` // (选填)消息发送方UserID(用于指定发送消息方帐号)
|
||||||
|
ToUserId string `json:"To_Account"` // (必填)消息接收方UserID
|
||||||
|
MsgLifeTime int `json:"MsgLifeTime,omitempty"` // (选填)消息离线保存时长(单位:秒),最长为7天(604800秒)
|
||||||
|
MsgSeq int `json:"MsgSeq,omitempty"` // (选填)消息序列号,后台会根据该字段去重及进行同秒内消息的排序,详细规则请看本接口的功能说明。若不填该字段,则由后台填入随机数。
|
||||||
|
MsgRandom uint32 `json:"MsgRandom"` // (必填)消息随机数,后台用于同一秒内的消息去重。请确保该字段填的是随机数
|
||||||
|
MsgTimeStamp int64 `json:"MsgTimeStamp,omitempty"` // (选填)消息时间戳,UNIX 时间戳(单位:秒)
|
||||||
|
MsgBody []*types.MsgBody `json:"MsgBody"` // (必填)消息内容,具体格式请参考 消息格式描述(注意,一条消息可包括多种消息元素,MsgBody 为 Array 类型)
|
||||||
|
SyncOtherMachine int `json:"SyncOtherMachine,omitempty"` // (选填)消息是否同步到在线终端和漫游上 1:把消息同步到 From_Account 在线终端和漫游上;2:消息不同步至 From_Account; 若不填写默认情况下会将消息存 From_Account 漫游
|
||||||
|
CloudCustomData string `json:"CloudCustomData,omitempty"` // (选填)消息回调禁止开关,只对本条消息有效,
|
||||||
|
SendMsgControl []string `json:"SendMsgControl,omitempty"` // (选填)消息发送控制选项,是一个 String 数组,只对本条消息有效。
|
||||||
|
ForbidCallbackControl []string `json:"ForbidCallbackControl,omitempty"` // (选填)消息回调禁止开关,只对本条消息有效
|
||||||
|
OfflinePushInfo *types.OfflinePushInfo `json:"OfflinePushInfo,omitempty"` // (选填)离线推送信息配置
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送消息(响应)
|
||||||
|
sendMessageResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
MsgTime int `json:"MsgTime"` // 消息时间戳,UNIX 时间戳
|
||||||
|
MsgKey string `json:"MsgKey"` // 消息唯一标识,用于撤回。长度不超过50个字符
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendMessageRet 发送消息结果
|
||||||
|
SendMessageRet struct {
|
||||||
|
MsgKey string // 消息唯一标识,用于撤回。长度不超过50个字符
|
||||||
|
MsgTime int // 消息时间戳,UNIX 时间戳
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量发单聊消息(请求)
|
||||||
|
sendMessagesReq struct {
|
||||||
|
FromUserId string `json:"From_Account,omitempty"` // (选填)消息发送方UserID(用于指定发送消息方帐号)
|
||||||
|
ToUserIds []string `json:"To_Account"` // (必填)消息接收方UserID
|
||||||
|
MsgSeq int `json:"MsgSeq,omitempty"` // (选填)消息序列号,后台会根据该字段去重及进行同秒内消息的排序,详细规则请看本接口的功能说明。若不填该字段,则由后台填入随机数。
|
||||||
|
MsgRandom uint32 `json:"MsgRandom"` // (必填)消息随机数,后台用于同一秒内的消息去重。请确保该字段填的是随机数
|
||||||
|
MsgBody []*types.MsgBody `json:"MsgBody"` // (必填)消息内容,具体格式请参考 消息格式描述(注意,一条消息可包括多种消息元素,MsgBody 为 Array 类型)
|
||||||
|
SyncOtherMachine int `json:"SyncOtherMachine,omitempty"` // (选填)消息是否同步到在线终端和漫游上 1:把消息同步到 From_Account 在线终端和漫游上;2:消息不同步至 From_Account; 若不填写默认情况下会将消息存 From_Account 漫游
|
||||||
|
CloudCustomData string `json:"CloudCustomData,omitempty"` // (选填)消息回调禁止开关,只对本条消息有效,
|
||||||
|
SendMsgControl []string `json:"SendMsgControl,omitempty"` // (选填)消息发送控制选项,是一个 String 数组,只对本条消息有效。
|
||||||
|
OfflinePushInfo *types.OfflinePushInfo `json:"OfflinePushInfo,omitempty"` // (选填)离线推送信息配置
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量发单聊消息(响应)
|
||||||
|
sendMessagesResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
MsgKey string `json:"MsgKey"`
|
||||||
|
Errors []SendMessageError `json:"ErrorList"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendMessageError 发送消息错误项
|
||||||
|
SendMessageError struct {
|
||||||
|
UserId string `json:"To_Account"`
|
||||||
|
ErrorCode int `json:"ErrorCode"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendMessagesRet 发送消息结果
|
||||||
|
SendMessagesRet struct {
|
||||||
|
MsgKey string
|
||||||
|
Errors []SendMessageError
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导入消息(请求)
|
||||||
|
importMessageReq struct {
|
||||||
|
FromUserId string `json:"From_Account,omitempty"` // (选填)消息发送方UserID(用于指定发送消息方帐号)
|
||||||
|
ToUserId string `json:"To_Account"` // (必填)消息接收方UserID
|
||||||
|
MsgSeq int `json:"MsgSeq,omitempty"` // (选填)消息序列号,后台会根据该字段去重及进行同秒内消息的排序,详细规则请看本接口的功能说明。若不填该字段,则由后台填入随机数。
|
||||||
|
MsgRandom uint32 `json:"MsgRandom"` // (必填)消息随机数,后台用于同一秒内的消息去重。请确保该字段填的是随机数
|
||||||
|
MsgTimeStamp int64 `json:"MsgTimeStamp,omitempty"` // (选填)消息时间戳,UNIX 时间戳(单位:秒)
|
||||||
|
MsgBody []*types.MsgBody `json:"MsgBody"` // (必填)消息内容,具体格式请参考 消息格式描述(注意,一条消息可包括多种消息元素,MsgBody 为 Array 类型)
|
||||||
|
SyncFromOldSystem int `json:"SyncFromOldSystem,omitempty"` // (选填)消息是否同步到在线终端和漫游上 1:把消息同步到 From_Account 在线终端和漫游上;2:消息不同步至 From_Account; 若不填写默认情况下会将消息存 From_Account 漫游
|
||||||
|
CloudCustomData string `json:"CloudCustomData,omitempty"` // (选填)消息回调禁止开关,只对本条消息有效,
|
||||||
|
}
|
||||||
|
|
||||||
|
// FetchMessagesArg 拉取消息参数
|
||||||
|
FetchMessagesArg struct {
|
||||||
|
FromUserId string `json:"From_Account"` // (必填)会话其中一方的 UserID,若已指定发送消息方帐号,则为消息发送方
|
||||||
|
ToUserId string `json:"To_Account"` // (必填)会话其中一方的 UserID
|
||||||
|
MaxLimited int `json:"MaxCnt"` // (必填)请求的消息条数
|
||||||
|
MinTime int64 `json:"MinTime"` // (必填)请求的消息时间范围的最小值
|
||||||
|
MaxTime int64 `json:"MaxTime"` // (必填)请求的消息时间范围的最大值
|
||||||
|
LastMsgKey string `json:"LastMsgKey,omitempty"` // (选填)上一次拉取到的最后一条消息的 MsgKey,续拉时需要填该字段,填写方法见上方
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拉取消息参数(响应)
|
||||||
|
fetchMessagesResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
Complete int `json:"Complete"` // 是否全部拉取,0表示未全部拉取,需要续拉,1表示已全部拉取
|
||||||
|
LastMsgTime int64 `json:"LastMsgTime"` // 本次拉取到的消息里的最后一条消息的时间
|
||||||
|
LastMsgKey string `json:"LastMsgKey"` // 本次拉取到的消息里的最后一条消息的标识
|
||||||
|
MsgCount int `json:"MsgCnt"` // 本次拉取到的消息条数
|
||||||
|
MsgList []*MessageItem `json:"MsgList"` // 消息列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// FetchMessagesRet 消息结果
|
||||||
|
FetchMessagesRet struct {
|
||||||
|
LastMsgTime int64 // 本次拉取到的消息里的最后一条消息的时间
|
||||||
|
LastMsgKey string // 本次拉取到的消息里的最后一条消息的标识
|
||||||
|
Count int // 本次拉取到的消息条数
|
||||||
|
HasMore bool // 是否还有更多数据
|
||||||
|
List []*MessageItem // 消息列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// MessageItem 消息项
|
||||||
|
MessageItem struct {
|
||||||
|
FromUserId string `json:"From_Account"`
|
||||||
|
ToUserId string `json:"To_Account"`
|
||||||
|
MsgSeq int `json:"MsgSeq"`
|
||||||
|
MsgRandom int `json:"MsgRandom"`
|
||||||
|
MsgTimeStamp int64 `json:"MsgTimeStamp"`
|
||||||
|
MsgFlagBits int `json:"MsgFlagBits"`
|
||||||
|
MsgKey string `json:"MsgKey"`
|
||||||
|
MsgBody []*types.MsgBody `json:"MsgBody"`
|
||||||
|
CloudCustomData string `json:"CloudCustomData"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PullMessagesArg 持续拉取单聊消息参数
|
||||||
|
PullMessagesArg struct {
|
||||||
|
FromUserId string `json:"From_Account"` // (必填)会话其中一方的 UserID,若已指定发送消息方帐号,则为消息发送方
|
||||||
|
ToUserId string `json:"To_Account"` // (必填)会话其中一方的 UserID
|
||||||
|
MaxLimited int `json:"MaxCnt"` // (必填)请求的消息条数
|
||||||
|
MinTime int64 `json:"MinTime"` // (必填)请求的消息时间范围的最小值
|
||||||
|
MaxTime int64 `json:"MaxTime"` // (必填)请求的消息时间范围的最大值
|
||||||
|
}
|
||||||
|
|
||||||
|
// 撤销消息(请求)
|
||||||
|
revokeMessageReq struct {
|
||||||
|
FromUserId string `json:"From_Account"` // (必填)消息发送方UserID
|
||||||
|
ToUserId string `json:"To_Account"` // (必填)消息接收方UserID
|
||||||
|
MsgKey string `json:"MsgKey"` // (必填)待撤回消息的唯一标识。该字段由 REST API 接口 单发单聊消息 和 批量发单聊消息 返回
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置单聊消息已读(请求)
|
||||||
|
setMessageReadReq struct {
|
||||||
|
UserId string `json:"Report_Account"` // (必填)进行消息已读的用户UserId
|
||||||
|
PeerUserId string `json:"Peer_Account"` // (必填)进行消息已读的单聊会话的另一方用户UserId
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询单聊未读消息计数(请求)
|
||||||
|
getUnreadMessageNumReq struct {
|
||||||
|
UserId string `json:"To_Account"` // (必填)待查询的用户UserId
|
||||||
|
PeerUserIds []string `json:"Peer_Account,omitempty"` // (选填)待查询的单聊会话对端的用户UserId
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询单聊未读消息计数(响应)
|
||||||
|
getUnreadMessageNumResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
AllUnreadMsgNum int `json:"AllC2CUnreadMsgNum"` // 单聊消息总未读数
|
||||||
|
PeerUnreadMsgNums []unreadMessageNum `json:"C2CUnreadMsgNumList"` // 单聊消息未读对端列表
|
||||||
|
PeerErrors []*UnreadMessageError `json:"ErrorList"` // 查询错误列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// 未读消息数
|
||||||
|
unreadMessageNum struct {
|
||||||
|
UserId string `json:"Peer_Account"` // 单聊会话对端UserId
|
||||||
|
UnreadMsgNum int `json:"C2CUnreadMsgNum"` // 该单聊会话的未读数
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询错误项
|
||||||
|
UnreadMessageError struct {
|
||||||
|
UserId string `json:"Peer_Account"` // 查询错误的目标UserId
|
||||||
|
ErrorCode int `json:"ErrorCode"` // 查询错误的错误码。若目标帐号的错误码为70107表示该帐号不存在
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUnreadMessageNumRet 未读消息结果
|
||||||
|
GetUnreadMessageNumRet struct {
|
||||||
|
Total int // 单聊消息总未读数
|
||||||
|
Results map[string]int // 未读消息数列表
|
||||||
|
Errors []*UnreadMessageError // 错误消息列表
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageInfo = types.ImageInfo
|
||||||
|
MsgTextContent = types.MsgTextContent
|
||||||
|
MsgFaceContent = types.MsgFaceContent
|
||||||
|
MsgFileContent = types.MsgFileContent
|
||||||
|
MsgImageContent = types.MsgImageContent
|
||||||
|
MsgSoundContent = types.MsgSoundContent
|
||||||
|
MsgVideoContent = types.MsgVideoContent
|
||||||
|
MsgCustomContent = types.MsgCustomContent
|
||||||
|
MsgLocationContent = types.MsgLocationContent
|
||||||
|
)
|
106
profile/api.go
Normal file
106
profile/api.go
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Author:1711788888@qq.com
|
||||||
|
* @Date: 2021/5/27 20:44
|
||||||
|
* @Desc: 资料管理
|
||||||
|
*/
|
||||||
|
|
||||||
|
package profile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/core"
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/enum"
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
service = "profile"
|
||||||
|
commandSetProfile = "portrait_set"
|
||||||
|
commandGetProfiles = "portrait_get"
|
||||||
|
)
|
||||||
|
|
||||||
|
type API interface {
|
||||||
|
// SetProfile 设置资料
|
||||||
|
// 支持 标配资料字段 和 自定义资料字段 的设置
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/1640
|
||||||
|
SetProfile(profile *Profile) (err error)
|
||||||
|
|
||||||
|
// GetProfiles 拉取资料
|
||||||
|
// 支持拉取好友和非好友的资料字段。
|
||||||
|
// 支持拉取 标配资料字段 和 自定义资料字段。
|
||||||
|
// 建议每次拉取的用户数不超过100,避免因回包数据量太大导致回包失败。
|
||||||
|
// 请确保请求中的所有帐号都已导入即时通信 IM,如果请求中含有未导入即时通信 IM 的帐号,即时通信 IM 后台将会提示错误。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/1639
|
||||||
|
GetProfiles(userIds []string, attrs []string) (profiles []*Profile, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type api struct {
|
||||||
|
client core.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAPI(client core.Client) API {
|
||||||
|
return &api{client: client}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetProfile 设置资料
|
||||||
|
// 支持 标配资料字段 和 自定义资料字段 的设置
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/1640
|
||||||
|
func (a *api) SetProfile(profile *Profile) (err error) {
|
||||||
|
if err = profile.CheckError(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userId := profile.GetUserId()
|
||||||
|
|
||||||
|
attrs := profile.GetAttrs()
|
||||||
|
|
||||||
|
if len(attrs) == 0 {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, "the attributes is not set")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &setProfileReq{UserId: userId, Attrs: make([]*types.TagPair, 0, len(attrs))}
|
||||||
|
|
||||||
|
for tag, value := range attrs {
|
||||||
|
req.Attrs = append(req.Attrs, &types.TagPair{
|
||||||
|
Tag: tag,
|
||||||
|
Value: value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandSetProfile, req, &types.ActionBaseResp{}); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetProfiles 拉取资料
|
||||||
|
// 支持拉取好友和非好友的资料字段。
|
||||||
|
// 支持拉取 标配资料字段 和 自定义资料字段。
|
||||||
|
// 建议每次拉取的用户数不超过100,避免因回包数据量太大导致回包失败。
|
||||||
|
// 请确保请求中的所有帐号都已导入即时通信 IM,如果请求中含有未导入即时通信 IM 的帐号,即时通信 IM 后台将会提示错误。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/1639
|
||||||
|
func (a *api) GetProfiles(userIds []string, attrs []string) (profiles []*Profile, err error) {
|
||||||
|
req := &getProfileReq{UserIds: userIds, TagList: attrs}
|
||||||
|
resp := &getProfileResp{}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandGetProfiles, req, resp); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, account := range resp.UserProfiles {
|
||||||
|
p := NewProfile(account.UserId)
|
||||||
|
p.SetError(account.ResultCode, account.ResultInfo)
|
||||||
|
for _, item := range account.Profile {
|
||||||
|
p.SetAttr(item.Tag, item.Value)
|
||||||
|
}
|
||||||
|
profiles = append(profiles, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
54
profile/enum.go
Normal file
54
profile/enum.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Author:1711788888@qq.com
|
||||||
|
* @Date: 2021/9/7 14:03
|
||||||
|
* @Desc: TODO
|
||||||
|
*/
|
||||||
|
|
||||||
|
package profile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/enum"
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// GenderType 性别类型
|
||||||
|
GenderType = types.GenderType
|
||||||
|
|
||||||
|
// AllowType 加好友验证方式
|
||||||
|
AllowType = types.AllowType
|
||||||
|
|
||||||
|
// AdminForbidType 管理员禁止加好友标识类型
|
||||||
|
AdminForbidType = types.AdminForbidType
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// 性别类型
|
||||||
|
GenderTypeUnknown = enum.GenderTypeUnknown // 没设置性别
|
||||||
|
GenderTypeFemale = enum.GenderTypeFemale // 女性
|
||||||
|
GenderTypeMale = enum.GenderTypeMale // 男性
|
||||||
|
|
||||||
|
// 加好友验证方式
|
||||||
|
AllowTypeNeedConfirm = enum.AllowTypeNeedConfirm // 需要经过自己确认对方才能添加自己为好友
|
||||||
|
AllowTypeAllowAny = enum.AllowTypeAllowAny // 允许任何人添加自己为好友
|
||||||
|
AllowTypeDenyAny = enum.AllowTypeDenyAny // 不允许任何人添加自己为好友
|
||||||
|
|
||||||
|
// 管理员禁止加好友标识类型
|
||||||
|
AdminForbidTypeNone = enum.AdminForbidTypeNone // 默认值,允许加好友
|
||||||
|
AdminForbidTypeSendOut = enum.AdminForbidTypeSendOut // 禁止该用户发起加好友请求
|
||||||
|
|
||||||
|
// 标准资料字段
|
||||||
|
StandardAttrNickname = enum.StandardAttrNickname // 昵称
|
||||||
|
StandardAttrGender = enum.StandardAttrGender // 性别
|
||||||
|
StandardAttrBirthday = enum.StandardAttrBirthday // 生日
|
||||||
|
StandardAttrLocation = enum.StandardAttrLocation // 所在地
|
||||||
|
StandardAttrSignature = enum.StandardAttrSignature // 个性签名
|
||||||
|
StandardAttrAllowType = enum.StandardAttrAllowType // 加好友验证方式
|
||||||
|
StandardAttrLanguage = enum.StandardAttrLanguage // 语言
|
||||||
|
StandardAttrAvatar = enum.StandardAttrAvatar // 头像URL
|
||||||
|
StandardAttrMsgSettings = enum.StandardAttrMsgSettings // 消息设置
|
||||||
|
StandardAttrAdminForbidType = enum.StandardAttrAdminForbidType // 管理员禁止加好友标识
|
||||||
|
StandardAttrLevel = enum.StandardAttrLevel // 等级
|
||||||
|
StandardAttrRole = enum.StandardAttrRole // 角色
|
||||||
|
)
|
39
profile/profile.go
Normal file
39
profile/profile.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Email:1711788888@qq.com
|
||||||
|
* @Date: 2021/8/28 11:23 上午
|
||||||
|
* @Desc: TODO
|
||||||
|
*/
|
||||||
|
|
||||||
|
package profile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/core"
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/entity"
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Profile struct {
|
||||||
|
entity.User
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewProfile(userId ...string) *Profile {
|
||||||
|
p := &Profile{}
|
||||||
|
if len(userId) > 0 {
|
||||||
|
p.SetUserId(userId[0])
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckError 检测错误
|
||||||
|
func (p *Profile) CheckError() (err error) {
|
||||||
|
if userId := p.GetUserId(); userId == "" {
|
||||||
|
return core.NewError(enum.InvalidParamsCode, "the userid is not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = p.GetError(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
39
profile/types.go
Normal file
39
profile/types.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Author:1711788888@qq.com
|
||||||
|
* @Date: 2021/5/29 17:38
|
||||||
|
* @Desc: 资料管理结构体定义
|
||||||
|
*/
|
||||||
|
|
||||||
|
package profile
|
||||||
|
|
||||||
|
import "git.echol.cn/loser/tencent-im/internal/types"
|
||||||
|
|
||||||
|
type (
|
||||||
|
// 设置资料(请求)
|
||||||
|
setProfileReq struct {
|
||||||
|
UserId string `json:"From_Account"` // (必填)需要设置该 UserID 的资料
|
||||||
|
Attrs []*types.TagPair `json:"ProfileItem"` // (必填)待设置的用户的资料对象数组
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取资料(请求)
|
||||||
|
getProfileReq struct {
|
||||||
|
UserIds []string `json:"To_Account"` // (必填)需要拉取这些UserID的资料
|
||||||
|
TagList []string `json:"TagList"` // (必填)指定要拉取的资料字段的 Tag,支持的字段有
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取资料(响应)
|
||||||
|
getProfileResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
ErrorDisplay string `json:"ErrorDisplay"` // 详细的客户端展示信息
|
||||||
|
UserProfiles []UserProfile `json:"UserProfileItem"` // 用户资料结构化信息
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserProfile 用户资料
|
||||||
|
UserProfile struct {
|
||||||
|
UserId string `json:"To_Account"` // 用户的UserID
|
||||||
|
Profile []types.TagPair `json:"ProfileItem"` // 用户的资料对象数组
|
||||||
|
ResultCode int `json:"ResultCode"` // 处理结果,0表示成功,非0表示失败
|
||||||
|
ResultInfo string `json:"ResultInfo"` // 错误描述信息,成功时该字段为空
|
||||||
|
}
|
||||||
|
)
|
398
push/api.go
Normal file
398
push/api.go
Normal file
@ -0,0 +1,398 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Author:1711788888@qq.com
|
||||||
|
* @Date: 2021/5/27 20:42
|
||||||
|
* @Desc: 全员推送
|
||||||
|
*/
|
||||||
|
|
||||||
|
package push
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/core"
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/enum"
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
service = "all_member_push"
|
||||||
|
commandPushMessage = "im_push"
|
||||||
|
commandSetAttrNames = "im_set_attr_name"
|
||||||
|
commandGetAttrNames = "im_get_attr_name"
|
||||||
|
commandGetUserAttrs = "im_get_attr"
|
||||||
|
commandSetUserAttrs = "im_set_attr"
|
||||||
|
commandDeleteUserAttrs = "im_remove_attr"
|
||||||
|
commandGetUserTags = "im_get_tag"
|
||||||
|
commandAddUserTags = "im_add_tag"
|
||||||
|
commandDeleteUserTags = "im_remove_tag"
|
||||||
|
commandDeleteUserAllTags = "im_remove_all_tags"
|
||||||
|
|
||||||
|
batchSetAttrNamesLimit = 10 // 批量设置应用属性名限制
|
||||||
|
batchGetUserAttrsLimit = 100 // 批量获取用户属性限制
|
||||||
|
batchSetUserAttrsLimit = 100 // 批量设置用户属性限制
|
||||||
|
batchDeleteUserAttrsLimit = 100 // 批量删除用户属性限制
|
||||||
|
batchAddUserTagsLimit = 100 // 批量添加用户标签限制
|
||||||
|
batchGetUserTagsLimit = 100 // 批量获取用户标签限制
|
||||||
|
batchDeleteUserTagsLimit = 100 // 批量删除用户标签限制
|
||||||
|
batchDeleteUserAllTagsUserLimit = 100 // 批量删除用户所有标签的用户限制
|
||||||
|
)
|
||||||
|
|
||||||
|
type API interface {
|
||||||
|
// PushMessage 全员推送
|
||||||
|
// 支持全员推送。
|
||||||
|
// 支持按用户属性推送。
|
||||||
|
// 支持按用户标签推送。
|
||||||
|
// 管理员推送消息,接收方看到消息发送者是管理员。
|
||||||
|
// 管理员指定某一帐号向其他帐号推送消息,接收方看到发送者不是管理员,而是管理员指定的帐号。
|
||||||
|
// 支持消息离线存储,不支持漫游。
|
||||||
|
// 由于全员推送需要下发的帐号数量巨大,下发完全部帐号需要一定时间(根据帐号总数而定,一般在一分钟内)。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/45934
|
||||||
|
PushMessage(message *Message) (taskId string, err error)
|
||||||
|
|
||||||
|
// SetAttrNames 设置应用属性名称
|
||||||
|
// 每个应用可以设置自定义的用户属性,最多可以有10个。通过本接口可以设置每个属性的名称,设置完成后,即可用于按用户属性推送等。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/45935
|
||||||
|
SetAttrNames(attrNames map[int]string) (err error)
|
||||||
|
|
||||||
|
// GetAttrNames 获取应用属性名称
|
||||||
|
// 管理员获取应用属性名称。使用前请先 设置应用属性名称 。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/45936
|
||||||
|
GetAttrNames() (attrNames map[int]string, err error)
|
||||||
|
|
||||||
|
// GetUserAttrs 获取用户属性
|
||||||
|
// 获取用户属性(必须以管理员帐号调用);每次最多只能获取100个用户的属性。使用前请先 设置应用属性名称 。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/45937
|
||||||
|
GetUserAttrs(userIds ...string) (attrs map[string]map[string]interface{}, err error)
|
||||||
|
|
||||||
|
// SetUserAttrs 设置用户属性
|
||||||
|
// 管理员给用户设置属性。每次最多只能给100个用户设置属性。使用前请先 设置应用属性名称 。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/45938
|
||||||
|
SetUserAttrs(userAttrs map[string]map[string]interface{}) (err error)
|
||||||
|
|
||||||
|
// DeleteUserAttrs 删除用户属性
|
||||||
|
// 管理员给用户删除属性。注意每次最多只能给100个用户删除属性。使用前请先 设置应用属性名称。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/45939
|
||||||
|
DeleteUserAttrs(userAttrs map[string][]string) (err error)
|
||||||
|
|
||||||
|
// GetUserTags 获取用户标签
|
||||||
|
// 获取用户标签(必须以管理员帐号调用)。每次最多只能获取100个用户的标签。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/45940
|
||||||
|
GetUserTags(userIds ...string) (tags map[string][]string, err error)
|
||||||
|
|
||||||
|
// AddUserTags 添加用户标签
|
||||||
|
// 管理员给用户添加标签。
|
||||||
|
// 每次请求最多只能给100个用户添加标签,请求体中单个用户添加标签数最多为10个。
|
||||||
|
// 单个用户可设置最大标签数为100个,若用户当前标签超过100,则添加新标签之前请先删除旧标签。
|
||||||
|
// 单个标签最大长度为50字节。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/45941
|
||||||
|
AddUserTags(userTags map[string][]string) (err error)
|
||||||
|
|
||||||
|
// DeleteUserTags 删除用户标签
|
||||||
|
// 管理员给用户删除标签。注意每次最多只能给100个用户删除标签。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/45942
|
||||||
|
DeleteUserTags(userTags map[string][]string) (err error)
|
||||||
|
|
||||||
|
// DeleteUserAllTags 删除用户所有标签
|
||||||
|
// 管理员给用户删除所有标签。注意每次最多只能给100个用户删除所有标签。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/45943
|
||||||
|
DeleteUserAllTags(userIds ...string) (err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type api struct {
|
||||||
|
client core.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAPI(client core.Client) API {
|
||||||
|
return &api{client: client}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushMessage 全员推送
|
||||||
|
// 支持全员推送。
|
||||||
|
// 支持按用户属性推送。
|
||||||
|
// 支持按用户标签推送。
|
||||||
|
// 管理员推送消息,接收方看到消息发送者是管理员。
|
||||||
|
// 管理员指定某一帐号向其他帐号推送消息,接收方看到发送者不是管理员,而是管理员指定的帐号。
|
||||||
|
// 支持消息离线存储,不支持漫游。
|
||||||
|
// 由于全员推送需要下发的帐号数量巨大,下发完全部帐号需要一定时间(根据帐号总数而定,一般在一分钟内)。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/45934
|
||||||
|
func (a *api) PushMessage(message *Message) (taskId string, err error) {
|
||||||
|
if err = message.checkError(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &pushMessageReq{}
|
||||||
|
req.FromUserId = message.GetSender()
|
||||||
|
req.MsgLifeTime = message.GetLifeTime()
|
||||||
|
req.OfflinePushInfo = message.GetOfflinePushInfo()
|
||||||
|
req.MsgBody = message.GetBody()
|
||||||
|
req.MsgRandom = message.GetRandom()
|
||||||
|
req.Condition = message.GetCondition()
|
||||||
|
|
||||||
|
resp := &pushMessageResp{}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandPushMessage, req, resp); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
taskId = resp.TaskId
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAttrNames 设置应用属性名称
|
||||||
|
// 每个应用可以设置自定义的用户属性,最多可以有10个。通过本接口可以设置每个属性的名称,设置完成后,即可用于按用户属性推送等。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/45935
|
||||||
|
func (a *api) SetAttrNames(attrNames map[int]string) (err error) {
|
||||||
|
if c := len(attrNames); c == 0 {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, "the attribute names is not set")
|
||||||
|
return
|
||||||
|
} else if c > batchSetAttrNamesLimit {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, fmt.Sprintf("the number of attribute names to be set cannot exceed %d", batchSetAttrNamesLimit))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &setAttrNamesReq{AttrNames: make(map[string]string, len(attrNames))}
|
||||||
|
|
||||||
|
for i, attrName := range attrNames {
|
||||||
|
req.AttrNames[strconv.Itoa(i)] = attrName
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandSetAttrNames, req, &types.ActionBaseResp{}); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAttrNames 获取应用属性名称
|
||||||
|
// 管理员获取应用属性名称。使用前请先 设置应用属性名称 。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/45936
|
||||||
|
func (a *api) GetAttrNames() (attrNames map[int]string, err error) {
|
||||||
|
req := &getAttrNamesReq{}
|
||||||
|
resp := &getAttrNamesResp{}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandGetAttrNames, req, resp); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(resp.AttrNames) > 0 {
|
||||||
|
var i int
|
||||||
|
attrNames = make(map[int]string, len(resp.AttrNames))
|
||||||
|
for key, attrName := range resp.AttrNames {
|
||||||
|
i, _ = strconv.Atoi(key)
|
||||||
|
attrNames[i] = attrName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserAttrs 获取用户属性
|
||||||
|
// 获取用户属性(必须以管理员帐号调用);每次最多只能获取100个用户的属性。使用前请先 设置应用属性名称 。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/45937
|
||||||
|
func (a *api) GetUserAttrs(userIds ...string) (attrs map[string]map[string]interface{}, err error) {
|
||||||
|
if c := len(userIds); c == 0 {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, "the accounts is not set")
|
||||||
|
return
|
||||||
|
} else if c > batchGetUserAttrsLimit {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, fmt.Sprintf("the number of accounts being queried cannot exceed %d", batchGetUserAttrsLimit))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &getUserAttrsReq{UserIds: userIds}
|
||||||
|
resp := &getUserAttrsResp{}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandGetUserAttrs, req, resp); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
attrs = make(map[string]map[string]interface{}, len(resp.Attrs))
|
||||||
|
for _, attr := range resp.Attrs {
|
||||||
|
attrs[attr.UserId] = attr.Attrs
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUserAttrs 设置用户属性
|
||||||
|
// 管理员给用户设置属性。每次最多只能给100个用户设置属性。使用前请先 设置应用属性名称 。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/45938
|
||||||
|
func (a *api) SetUserAttrs(userAttrs map[string]map[string]interface{}) (err error) {
|
||||||
|
if c := len(userAttrs); c == 0 {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, "the attributes is not set")
|
||||||
|
return
|
||||||
|
} else if c > batchSetUserAttrsLimit {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, fmt.Sprintf("the number of attributes to be set cannot exceed %d", batchSetUserAttrsLimit))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &setUserAttrsReq{Attrs: make([]*userAttrItem, 0, len(userAttrs))}
|
||||||
|
for userId, attrs := range userAttrs {
|
||||||
|
req.Attrs = append(req.Attrs, &userAttrItem{
|
||||||
|
UserId: userId,
|
||||||
|
Attrs: attrs,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandSetUserAttrs, req, &types.ActionBaseResp{}); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteUserAttrs 删除用户属性
|
||||||
|
// 管理员给用户删除属性。注意每次最多只能给100个用户删除属性。使用前请先 设置应用属性名称。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/45939
|
||||||
|
func (a *api) DeleteUserAttrs(userAttrs map[string][]string) (err error) {
|
||||||
|
if c := len(userAttrs); c == 0 {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, "the attributes is not set")
|
||||||
|
return
|
||||||
|
} else if c > batchDeleteUserAttrsLimit {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, fmt.Sprintf("the number of attributes to be delete cannot exceed %d", batchDeleteUserAttrsLimit))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &deleteUserAttrsReq{Attrs: make([]deleteUserAttr, 0, len(userAttrs))}
|
||||||
|
for userId, attrs := range userAttrs {
|
||||||
|
req.Attrs = append(req.Attrs, deleteUserAttr{
|
||||||
|
UserId: userId,
|
||||||
|
Attrs: attrs,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandDeleteUserAttrs, req, &types.ActionBaseResp{}); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserTags 获取用户标签
|
||||||
|
// 获取用户标签(必须以管理员帐号调用)。每次最多只能获取100个用户的标签。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/45940
|
||||||
|
func (a *api) GetUserTags(userIds ...string) (tags map[string][]string, err error) {
|
||||||
|
if c := len(userIds); c == 0 {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, "the accounts is not set")
|
||||||
|
return
|
||||||
|
} else if c > batchGetUserTagsLimit {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, fmt.Sprintf("the number of tags being queried cannot exceed %d", batchGetUserTagsLimit))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &getUserTagsReq{UserIds: userIds}
|
||||||
|
resp := &getUserTagsResp{}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandGetUserTags, req, resp); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(resp.Tags) > 0 {
|
||||||
|
tags = make(map[string][]string, len(resp.Tags))
|
||||||
|
for _, item := range resp.Tags {
|
||||||
|
tags[item.UserId] = item.Tags
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddUserTags 添加用户标签
|
||||||
|
// 管理员给用户添加标签。
|
||||||
|
// 每次请求最多只能给100个用户添加标签,请求体中单个用户添加标签数最多为10个。
|
||||||
|
// 单个用户可设置最大标签数为100个,若用户当前标签超过100,则添加新标签之前请先删除旧标签。
|
||||||
|
// 单个标签最大长度为50字节。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/45941
|
||||||
|
func (a *api) AddUserTags(userTags map[string][]string) (err error) {
|
||||||
|
if c := len(userTags); c == 0 {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, "the tags of user is not set")
|
||||||
|
return
|
||||||
|
} else if c > batchAddUserTagsLimit {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, fmt.Sprintf("the number of tags to be add cannot exceed %d", batchAddUserTagsLimit))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &addUserTagsReq{Tags: make([]*userTag, 0, len(userTags))}
|
||||||
|
for userId, tags := range userTags {
|
||||||
|
req.Tags = append(req.Tags, &userTag{
|
||||||
|
UserId: userId,
|
||||||
|
Tags: tags,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandAddUserTags, req, &types.ActionBaseResp{}); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteUserTags 删除用户标签
|
||||||
|
// 管理员给用户删除标签。注意每次最多只能给100个用户删除标签。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/45942
|
||||||
|
func (a *api) DeleteUserTags(userTags map[string][]string) (err error) {
|
||||||
|
if c := len(userTags); c == 0 {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, "the tags of user is not set")
|
||||||
|
return
|
||||||
|
} else if c > batchDeleteUserTagsLimit {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, fmt.Sprintf("the number of tags to be delete cannot exceed %d", batchDeleteUserTagsLimit))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &deleteUserTagsReq{Tags: make([]*userTag, 0, len(userTags))}
|
||||||
|
for userId, tags := range userTags {
|
||||||
|
req.Tags = append(req.Tags, &userTag{
|
||||||
|
UserId: userId,
|
||||||
|
Tags: tags,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandDeleteUserTags, req, &types.ActionBaseResp{}); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteUserAllTags 删除用户所有标签
|
||||||
|
// 管理员给用户删除所有标签。注意每次最多只能给100个用户删除所有标签。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/45943
|
||||||
|
func (a *api) DeleteUserAllTags(userIds ...string) (err error) {
|
||||||
|
if c := len(userIds); c == 0 {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, "the accounts is not set")
|
||||||
|
return
|
||||||
|
} else if c > batchDeleteUserAllTagsUserLimit {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, fmt.Sprintf("the number of accounts to be delete cannot exceed %d", batchDeleteUserAllTagsUserLimit))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &deleteUserAllTagsReq{UserIds: userIds}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandDeleteUserAllTags, req, &types.ActionBaseResp{}); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
38
push/enum.go
Normal file
38
push/enum.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Author:1711788888@qq.com
|
||||||
|
* @Date: 2021/9/7 10:16
|
||||||
|
* @Desc: TODO
|
||||||
|
*/
|
||||||
|
|
||||||
|
package push
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// 推送标识
|
||||||
|
PushFlagYes = enum.PushFlagYes // 正常推送
|
||||||
|
PushFlagNo = enum.PushFlagYes // 不离线推送
|
||||||
|
|
||||||
|
// 华为推送通知消息分类
|
||||||
|
HuaWeiImportanceLow = enum.HuaWeiImportanceLow // LOW类消息
|
||||||
|
HuaWeiImportanceNormal = enum.HuaWeiImportanceNormal // NORMAL类消息
|
||||||
|
|
||||||
|
// 华为推送为“打开应用内指定页面”的前提下透传参数行为
|
||||||
|
HuaweiIntentParamAction = enum.HuaweiIntentParamAction // 将透传内容Ext作为Action参数
|
||||||
|
HuaweiIntentParamIntent = enum.HuaweiIntentParamIntent // 将透传内容Ext作为Intent参数
|
||||||
|
|
||||||
|
// VIVO手机推送消息分类
|
||||||
|
VivoClassificationOperation = enum.VivoClassificationOperation // 运营类消息
|
||||||
|
VivoClassificationSystem = enum.VivoClassificationSystem // 系统类消息
|
||||||
|
|
||||||
|
// IOS徽章计数模式
|
||||||
|
BadgeModeNormal = enum.BadgeModeNormal // 本条消息需要计数
|
||||||
|
BadgeModeIgnore = enum.BadgeModeIgnore // 本条消息不需要计数
|
||||||
|
|
||||||
|
// IOS10的推送扩展开关
|
||||||
|
MutableContentNormal = enum.MutableContentNormal // 关闭iOS10的推送扩展
|
||||||
|
MutableContentEnable = enum.MutableContentEnable // 开启iOS10的推送扩展
|
||||||
|
)
|
152
push/message.go
Normal file
152
push/message.go
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Author:1711788888@qq.com
|
||||||
|
* @Date: 2021/9/3 18:15
|
||||||
|
* @Desc: 推送消息实体
|
||||||
|
*/
|
||||||
|
|
||||||
|
package push
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/entity"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errInvalidPushCondition = errors.New("attrs and tags condition cannot be set at the same time")
|
||||||
|
|
||||||
|
type Message struct {
|
||||||
|
entity.Message
|
||||||
|
condition *condition
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMessage() *Message {
|
||||||
|
return &Message{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetConditionTagsOr 设置标签的或条件(设置会冲掉之前的标签或条件)
|
||||||
|
func (m *Message) SetConditionTagsOr(tags ...string) {
|
||||||
|
if m.condition != nil && m.condition.TagsOr != nil {
|
||||||
|
m.condition.TagsOr = m.condition.TagsOr[0:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
m.AddConditionTagsOr(tags...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddConditionTagsOr 添加标签的或条件(添加会累加之前的条件或条件)
|
||||||
|
func (m *Message) AddConditionTagsOr(tags ...string) {
|
||||||
|
if m.condition == nil {
|
||||||
|
m.condition = &condition{}
|
||||||
|
}
|
||||||
|
if m.condition.TagsOr == nil {
|
||||||
|
m.condition.TagsOr = make([]string, 0)
|
||||||
|
}
|
||||||
|
m.condition.TagsOr = append(m.condition.TagsOr, tags...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetConditionTagsAnd 设置标签的与条件(设置会冲掉之前的标签与条件)
|
||||||
|
func (m *Message) SetConditionTagsAnd(tags ...string) {
|
||||||
|
if m.condition != nil && m.condition.TagsAnd != nil {
|
||||||
|
m.condition.TagsAnd = m.condition.TagsAnd[0:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
m.AddConditionTagsAnd(tags...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddConditionTagsAnd 添加标签的与条件(添加会累加之前的标签与条件)
|
||||||
|
func (m *Message) AddConditionTagsAnd(tags ...string) {
|
||||||
|
if m.condition == nil {
|
||||||
|
m.condition = &condition{}
|
||||||
|
}
|
||||||
|
if m.condition.TagsAnd == nil {
|
||||||
|
m.condition.TagsAnd = make([]string, 0)
|
||||||
|
}
|
||||||
|
m.condition.TagsAnd = append(m.condition.TagsAnd, tags...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetConditionAttrsOr 设置属性的或条件(设置会冲掉之前的属性或条件)
|
||||||
|
func (m *Message) SetConditionAttrsOr(attrs map[string]interface{}) {
|
||||||
|
if m.condition != nil && m.condition.AttrsOr != nil {
|
||||||
|
m.condition.AttrsOr = make(map[string]interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
m.AddConditionAttrsOr(attrs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddConditionAttrsOr 添加属性的或条件(添加会累加之前的属性或条件)
|
||||||
|
func (m *Message) AddConditionAttrsOr(attrs map[string]interface{}) {
|
||||||
|
if m.condition == nil {
|
||||||
|
m.condition = &condition{}
|
||||||
|
}
|
||||||
|
if m.condition.AttrsOr == nil {
|
||||||
|
m.condition.AttrsOr = make(map[string]interface{})
|
||||||
|
}
|
||||||
|
for k, v := range attrs {
|
||||||
|
m.condition.AttrsOr[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetConditionAttrsAnd 设置属性的与条件(设置会冲掉之前的属性与条件)
|
||||||
|
func (m *Message) SetConditionAttrsAnd(attrs map[string]interface{}) {
|
||||||
|
if m.condition != nil && m.condition.AttrsAnd != nil {
|
||||||
|
m.condition.AttrsAnd = make(map[string]interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
m.AddConditionAttrsAnd(attrs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddConditionAttrsAnd 添加属性的与条件(添加会累加之前的属性与条件)
|
||||||
|
func (m *Message) AddConditionAttrsAnd(attrs map[string]interface{}) {
|
||||||
|
if m.condition == nil {
|
||||||
|
m.condition = &condition{}
|
||||||
|
}
|
||||||
|
if m.condition.AttrsAnd == nil {
|
||||||
|
m.condition.AttrsAnd = make(map[string]interface{})
|
||||||
|
}
|
||||||
|
for k, v := range attrs {
|
||||||
|
m.condition.AttrsAnd[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCondition 获取推送条件
|
||||||
|
func (m *Message) GetCondition() *condition {
|
||||||
|
return m.condition
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkError 检测错误
|
||||||
|
func (m *Message) checkError() (err error) {
|
||||||
|
if err = m.CheckLifeTimeArgError(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = m.CheckBodyArgError(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = m.checkConditionArgError(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkConditionArgError 检测条件参数错误
|
||||||
|
func (m *Message) checkConditionArgError() error {
|
||||||
|
hasAttrs, hasTags := false, false
|
||||||
|
|
||||||
|
if m.condition != nil {
|
||||||
|
if m.condition.AttrsAnd != nil || m.condition.AttrsOr != nil {
|
||||||
|
hasAttrs = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.condition.TagsAnd != nil || m.condition.TagsOr != nil {
|
||||||
|
hasTags = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasAttrs && hasTags {
|
||||||
|
return errInvalidPushCondition
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
126
push/types.go
Normal file
126
push/types.go
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Author:1711788888@qq.com
|
||||||
|
* @Date: 2021/5/29 17:39
|
||||||
|
* @Desc: Push Api Request And Response Type Definition.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package push
|
||||||
|
|
||||||
|
import "git.echol.cn/loser/tencent-im/internal/types"
|
||||||
|
|
||||||
|
type (
|
||||||
|
// 推送条件
|
||||||
|
condition struct {
|
||||||
|
TagsAnd []string `json:"TagsAnd"` // (选填)标签条件的交集。标签是一个不超过50字节的字符串。注意属性推送和标签推送不可同时作为推送条件。TagsAnd 条件中的标签个数不能超过10个
|
||||||
|
TagsOr []string `json:"TagsOr"` // (选填)标签条件的并集。标签是一个不超过50字节的字符串。注意属性推送和标签推送不可同时作为推送条件。TagsOr 条件中的标签个数不能超过10个
|
||||||
|
AttrsAnd map[string]interface{} `json:"AttrsAnd"` // (选填)属性条件的交集。注意属性推送和标签推送不可同时作为推送条件
|
||||||
|
AttrsOr map[string]interface{} `json:"AttrsOr"` // (选填)属性条件的并集。注意属性推送和标签推送不可同时作为推送条件
|
||||||
|
}
|
||||||
|
|
||||||
|
// 推送(请求)
|
||||||
|
pushMessageReq struct {
|
||||||
|
FromUserId string `json:"From_Account,omitempty"` // (选填)消息推送方帐号
|
||||||
|
Condition *condition `json:"Condition,omitempty"` // (选填)推送条件
|
||||||
|
MsgRandom uint32 `json:"MsgRandom"` // (必填)消息随机数,由随机函数产生
|
||||||
|
MsgBody []*types.MsgBody `json:"MsgBody"` // (必填)消息内容
|
||||||
|
MsgLifeTime int `json:"MsgLifeTime,omitempty"` // (选填)消息离线存储时间,单位秒,最多保存7天(604800秒)。默认为0,表示不离线存储
|
||||||
|
OfflinePushInfo *types.OfflinePushInfo `json:"OfflinePushInfo,omitempty"` // (选填)离线推送信息配置
|
||||||
|
}
|
||||||
|
|
||||||
|
// 推送(响应)
|
||||||
|
pushMessageResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
TaskId string `json:"TaskId"` // 推送任务ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置应用属性名称(请求)
|
||||||
|
setAttrNamesReq struct {
|
||||||
|
AttrNames map[string]string `json:"AttrNames"` // (必填)属性名
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取应用属性名称(请求)
|
||||||
|
getAttrNamesReq struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取应用属性名称(响应)
|
||||||
|
getAttrNamesResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
AttrNames map[string]string `json:"AttrNames"` // 属性名
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户属性(请求)
|
||||||
|
getUserAttrsReq struct {
|
||||||
|
UserIds []string `json:"To_Account"` // (必填)目标用户帐号列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户属性(响应)
|
||||||
|
getUserAttrsResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
Attrs []*userAttrItem `json:"Attrs"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用户属性
|
||||||
|
userAttrItem struct {
|
||||||
|
UserId string `json:"To_Account"` // 用户UserId
|
||||||
|
Attrs map[string]interface{} `json:"Attrs"` // 用户属性
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置用户属性(请求)
|
||||||
|
setUserAttrsReq struct {
|
||||||
|
Attrs []*userAttrItem `json:"Attrs"` // (必填)用户属性
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除用户属性(请求)
|
||||||
|
deleteUserAttrsReq struct {
|
||||||
|
Attrs []deleteUserAttr `json:"Attrs"` // (必填)用户属性
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除的用户属性
|
||||||
|
deleteUserAttr struct {
|
||||||
|
UserId string `json:"To_Account"` // 用户UserId
|
||||||
|
Attrs []string `json:"Attrs"` // 用户属性
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户标签(请求)
|
||||||
|
getUserTagsReq struct {
|
||||||
|
UserIds []string `json:"To_Account"` // (必填)目标用户帐号列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户标签(响应)
|
||||||
|
getUserTagsResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
Tags []userTag `json:"Tags"` // 用户标签内容列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用户标签
|
||||||
|
userTag struct {
|
||||||
|
UserId string `json:"To_Account"`
|
||||||
|
Tags []string `json:"Tags"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加用户标签(请求)
|
||||||
|
addUserTagsReq struct {
|
||||||
|
Tags []*userTag `json:"Tags"` // (必填)用户标签内容列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除用户标签(请求)
|
||||||
|
deleteUserTagsReq struct {
|
||||||
|
Tags []*userTag `json:"Tags"` // (必填)用户标签内容列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除用户所有标签(请求)
|
||||||
|
deleteUserAllTagsReq struct {
|
||||||
|
UserIds []string `json:"To_Account"`
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageInfo = types.ImageInfo
|
||||||
|
MsgTextContent = types.MsgTextContent
|
||||||
|
MsgFaceContent = types.MsgFaceContent
|
||||||
|
MsgFileContent = types.MsgFileContent
|
||||||
|
MsgImageContent = types.MsgImageContent
|
||||||
|
MsgSoundContent = types.MsgSoundContent
|
||||||
|
MsgVideoContent = types.MsgVideoContent
|
||||||
|
MsgCustomContent = types.MsgCustomContent
|
||||||
|
MsgLocationContent = types.MsgLocationContent
|
||||||
|
)
|
148
recentcontact/api.go
Normal file
148
recentcontact/api.go
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
/**
|
||||||
|
* @Author: wanglin
|
||||||
|
* @Author: wanglin@vspn.com
|
||||||
|
* @Date: 2021/10/28 16:05
|
||||||
|
* @Desc: TODO
|
||||||
|
*/
|
||||||
|
|
||||||
|
package recentcontact
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/core"
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
service = "recentcontact"
|
||||||
|
commandFetchSessions = "get_list"
|
||||||
|
commandDeleteSession = "delete"
|
||||||
|
)
|
||||||
|
|
||||||
|
type API interface {
|
||||||
|
// FetchSessions 拉取会话列表
|
||||||
|
// 支持分页拉取会话列表
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/62118
|
||||||
|
FetchSessions(arg *FetchSessionsArg) (ret *FetchSessionsRet, err error)
|
||||||
|
|
||||||
|
// PullSessions 续拉取会话列表
|
||||||
|
// 本API是借助"拉取会话列表"API进行扩展实现
|
||||||
|
// 支持分页拉取会话列表
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/62118
|
||||||
|
PullSessions(arg *PullSessionsArg, fn func(ret *FetchSessionsRet)) (err error)
|
||||||
|
|
||||||
|
// DeleteSession 删除单个会话
|
||||||
|
// 删除指定会话,支持同步清理漫游消息。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/62119
|
||||||
|
DeleteSession(fromUserId, toUserId string, SessionType SessionType, isClearRamble ...bool) (err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type api struct {
|
||||||
|
client core.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAPI(client core.Client) API {
|
||||||
|
return &api{client: client}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FetchSessions 拉取会话列表
|
||||||
|
// 支持分页拉取会话列表
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/62118
|
||||||
|
func (a *api) FetchSessions(arg *FetchSessionsArg) (ret *FetchSessionsRet, err error) {
|
||||||
|
req := &fetchSessionsReq{
|
||||||
|
UserId: arg.UserId,
|
||||||
|
TimeStamp: arg.TimeStamp,
|
||||||
|
StartIndex: arg.StartIndex,
|
||||||
|
TopTimeStamp: arg.TopTimeStamp,
|
||||||
|
TopStartIndex: arg.TopStartIndex,
|
||||||
|
}
|
||||||
|
|
||||||
|
if arg.IsAllowTopSession {
|
||||||
|
req.AssistFlags += 1 << 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if arg.IsReturnEmptySession {
|
||||||
|
req.AssistFlags += 1 << 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if arg.IsAllowTopSessionPaging {
|
||||||
|
req.AssistFlags += 1 << 2
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := &fetchSessionsResp{}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandFetchSessions, req, resp); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = &FetchSessionsRet{
|
||||||
|
TimeStamp: resp.TimeStamp,
|
||||||
|
StartIndex: resp.StartIndex,
|
||||||
|
TopTimeStamp: resp.TopTimeStamp,
|
||||||
|
TopStartIndex: resp.TopStartIndex,
|
||||||
|
List: resp.Sessions,
|
||||||
|
HasMore: resp.CompleteFlag == 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// PullSessions 续拉取会话列表
|
||||||
|
// 本API是借助"拉取会话列表"API进行扩展实现
|
||||||
|
// 支持分页拉取会话列表
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/62118
|
||||||
|
func (a *api) PullSessions(arg *PullSessionsArg, fn func(ret *FetchSessionsRet)) (err error) {
|
||||||
|
var (
|
||||||
|
ret *FetchSessionsRet
|
||||||
|
req = &FetchSessionsArg{
|
||||||
|
UserId: arg.UserId,
|
||||||
|
IsAllowTopSession: arg.IsAllowTopSession,
|
||||||
|
IsReturnEmptySession: arg.IsReturnEmptySession,
|
||||||
|
IsAllowTopSessionPaging: arg.IsAllowTopSessionPaging,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
for ret == nil || ret.HasMore {
|
||||||
|
ret, err = a.FetchSessions(req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fn(ret)
|
||||||
|
|
||||||
|
if ret.HasMore {
|
||||||
|
req.TimeStamp = ret.TimeStamp
|
||||||
|
req.StartIndex = ret.StartIndex
|
||||||
|
req.TopTimeStamp = ret.TopTimeStamp
|
||||||
|
req.TopStartIndex = ret.TopStartIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteSession 删除单个会话
|
||||||
|
// 删除指定会话,支持同步清理漫游消息。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/62119
|
||||||
|
func (a *api) DeleteSession(fromUserId, toUserId string, SessionType SessionType, isClearRamble ...bool) (err error) {
|
||||||
|
req := &deleteSessionReq{
|
||||||
|
FromUserId: fromUserId,
|
||||||
|
ToUserId: toUserId,
|
||||||
|
Type: SessionType,
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(isClearRamble) > 0 && isClearRamble[0] {
|
||||||
|
req.ClearRamble = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandDeleteSession, req, &types.ActionBaseResp{}); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
16
recentcontact/enum.go
Normal file
16
recentcontact/enum.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* @Author: wanglin
|
||||||
|
* @Author: wanglin@vspn.com
|
||||||
|
* @Date: 2021/10/28 17:22
|
||||||
|
* @Desc: TODO
|
||||||
|
*/
|
||||||
|
|
||||||
|
package recentcontact
|
||||||
|
|
||||||
|
// SessionType 会话类型
|
||||||
|
type SessionType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
SessionTypeC2C SessionType = 1 // C2C 会话
|
||||||
|
SessionTypeG2C SessionType = 2 // G2C 会话
|
||||||
|
)
|
78
recentcontact/types.go
Normal file
78
recentcontact/types.go
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
/**
|
||||||
|
* @Author: wanglin
|
||||||
|
* @Author: wanglin@vspn.com
|
||||||
|
* @Date: 2021/10/28 16:11
|
||||||
|
* @Desc: TODO
|
||||||
|
*/
|
||||||
|
|
||||||
|
package recentcontact
|
||||||
|
|
||||||
|
import "git.echol.cn/loser/tencent-im/internal/types"
|
||||||
|
|
||||||
|
// FetchSessionsArg 拉取会话列表(参数)
|
||||||
|
type FetchSessionsArg struct {
|
||||||
|
UserId string // (必填)请求拉取该用户的会话列表
|
||||||
|
TimeStamp int // (必填)普通会话的起始时间,第一页填 0
|
||||||
|
StartIndex int // (必填)普通会话的起始位置,第一页填 0
|
||||||
|
TopTimeStamp int // (必填)置顶会话的起始时间,第一页填 0
|
||||||
|
TopStartIndex int // (必填)置顶会话的起始位置,第一页填 0
|
||||||
|
IsAllowTopSession bool // (选填)是否支持置顶会话
|
||||||
|
IsReturnEmptySession bool // (选填)是否返回空会话
|
||||||
|
IsAllowTopSessionPaging bool // (选填)是否支持置顶会话分页
|
||||||
|
}
|
||||||
|
|
||||||
|
// PullSessionsArg 续拉取会话列表(参数)
|
||||||
|
type PullSessionsArg struct {
|
||||||
|
UserId string // (必填)请求拉取该用户的会话列表
|
||||||
|
IsAllowTopSession bool // (选填)是否支持置顶会话
|
||||||
|
IsReturnEmptySession bool // (选填)是否返回空会话
|
||||||
|
IsAllowTopSessionPaging bool // (选填)是否支持置顶会话分页
|
||||||
|
}
|
||||||
|
|
||||||
|
// FetchSessionsRet 拉取会话列表(返回)
|
||||||
|
type FetchSessionsRet struct {
|
||||||
|
TimeStamp int // 普通会话下一页拉取的起始时间,分页拉取时通过请求包的 TimeStamp 字段带给移动通信后台
|
||||||
|
StartIndex int // 普通会话下一页拉取的起始位置,分页拉取时通过请求包的 StartIndex 字段带给移动通信后台
|
||||||
|
TopTimeStamp int // 置顶会话下一页拉取的起始时间,分页拉取时通过请求包的 TopTimeStamp 字段带给移动通信后台
|
||||||
|
TopStartIndex int // 置顶会话下一页拉取的起始位置,分页拉取时通过请求包的 TopStartIndex 字段带给移动通信后台
|
||||||
|
HasMore bool // 是否拉完了数据
|
||||||
|
List []*SessionItem // 会话对象数组
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetchSessionsReq 拉取会话列表(请求)
|
||||||
|
type fetchSessionsReq struct {
|
||||||
|
UserId string `json:"From_Account"` // (必填)请求拉取该用户的会话列表
|
||||||
|
TimeStamp int `json:"TimeStamp"` // (必填)普通会话的起始时间,第一页填 0
|
||||||
|
StartIndex int `json:"StartIndex"` // (必填)普通会话的起始位置,第一页填 0
|
||||||
|
TopTimeStamp int `json:"TopTimeStamp"` // (必填)置顶会话的起始时间,第一页填 0
|
||||||
|
TopStartIndex int `json:"TopStartIndex"` // (必填)置顶会话的起始位置,第一页填 0
|
||||||
|
AssistFlags int `json:"AssistFlags"` // (必填)会话辅助标志位(bit 0 - 是否支持置顶会话;bit 1 - 是否返回空会话;bit 2 - 是否支持置顶会话分页)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetchSessionsResp 拉取会话列表(响应)
|
||||||
|
type fetchSessionsResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
CompleteFlag int `json:"CompleteFlag"` // 结束标识:1 表示已返回全量会话,0 表示还有会话没拉完
|
||||||
|
TimeStamp int `json:"TimeStamp"` // 普通会话下一页拉取的起始时间,分页拉取时通过请求包的 TimeStamp 字段带给移动通信后台
|
||||||
|
StartIndex int `json:"StartIndex"` // 普通会话下一页拉取的起始位置,分页拉取时通过请求包的 StartIndex 字段带给移动通信后台
|
||||||
|
TopTimeStamp int `json:"TopTimeStamp"` // 置顶会话下一页拉取的起始时间,分页拉取时通过请求包的 TopTimeStamp 字段带给移动通信后台
|
||||||
|
TopStartIndex int `json:"TopStartIndex"` // 置顶会话下一页拉取的起始位置,分页拉取时通过请求包的 TopStartIndex 字段带给移动通信后台
|
||||||
|
Sessions []*SessionItem `json:"SessionItem"` // 会话对象数组
|
||||||
|
}
|
||||||
|
|
||||||
|
// SessionItem 会话对象
|
||||||
|
type SessionItem struct {
|
||||||
|
Type SessionType `json:"Type"` // 会话类型:1 表示 C2C 会话;2 表示 G2C 会话
|
||||||
|
UserId string `json:"To_Account,omitempty"` // C2C 会话才会返回,返回会话方的 UserID
|
||||||
|
GroupId string `json:"GroupId,omitempty"` // G2C 会话才会返回,返回群 ID
|
||||||
|
MsgTime int `json:"MsgTime"` // 会话时间
|
||||||
|
TopFlag int `json:"TopFlag"` // 置顶标记:0 标识普通会话;1 标识置顶会话
|
||||||
|
}
|
||||||
|
|
||||||
|
// deleteSessionReq 删除单个会话(请求)
|
||||||
|
type deleteSessionReq struct {
|
||||||
|
FromUserId string `json:"From_Account"` // (必填)请求删除该 UserID 的会话
|
||||||
|
Type SessionType `json:"type"` // (必填)会话类型:1 表示 C2C 会话;2 表示 G2C 会话
|
||||||
|
ToUserId string `json:"To_Account"` // (必填)待删除的会话的 UserID
|
||||||
|
ClearRamble int `json:"ClearRamble,omitempty"` // (选填)是否清理漫游消息:1 表示清理漫游消息;0 表示不清理漫游消息
|
||||||
|
}
|
901
sns/api.go
Normal file
901
sns/api.go
Normal file
@ -0,0 +1,901 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Author:1711788888@qq.com
|
||||||
|
* @Date: 2021/5/27 20:45
|
||||||
|
* @Desc: 关系链管理
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/core"
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/enum"
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
service = "sns"
|
||||||
|
commandAddFriend = "friend_add"
|
||||||
|
commandImportFriend = "friend_import"
|
||||||
|
commandUpdateFriend = "friend_update"
|
||||||
|
commandDeleteFriend = "friend_delete"
|
||||||
|
commandDeleteAllFriend = "friend_delete_all"
|
||||||
|
commandCheckFriend = "friend_check"
|
||||||
|
commandGetFriend = "friend_get_list"
|
||||||
|
commandFetchFriend = "friend_get"
|
||||||
|
commandAddBlackList = "black_list_add"
|
||||||
|
commandDeleteBlackList = "black_list_delete"
|
||||||
|
commandGetBlackList = "black_list_get"
|
||||||
|
commandCheckBlackList = "black_list_check"
|
||||||
|
commandAddGroup = "group_add"
|
||||||
|
commandDeleteGroup = "group_delete"
|
||||||
|
commandGetGroup = "group_get"
|
||||||
|
|
||||||
|
batchCheckFriendsLimit = 100 // 批量校验好友限制
|
||||||
|
batchGetFriendsLimit = 100 // 批量获取好友限制
|
||||||
|
batchAddBlacklistLimit = 1000 // 批量添加黑名单限制
|
||||||
|
batchDeleteBlacklistLimit = 1000 // 批量删除黑名单限制
|
||||||
|
batchCheckBlacklistLimit = 1000 // 批量校验黑名单限制
|
||||||
|
batchAddGroupsLimit = 100 // 批量添加分组限制
|
||||||
|
batchJoinGroupsLimit = 1000 // 批量加入群组账号限制
|
||||||
|
batchDeleteGroupsLimit = 100 // 批量删除分组限制
|
||||||
|
batchGetGroupsLimit = 100 // 批量获取分组限制
|
||||||
|
)
|
||||||
|
|
||||||
|
type API interface {
|
||||||
|
// AddFriend 添加单个好友
|
||||||
|
// 本方法拓展于“添加多个好友(AddFriends)”方法。
|
||||||
|
// 添加好友,仅支持添加单个好友
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/1643
|
||||||
|
AddFriend(userId string, isBothAdd, isForceAdd bool, friend *Friend) (err error)
|
||||||
|
|
||||||
|
// AddFriends 添加多个好友
|
||||||
|
// 添加好友,支持批量添加好友
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/1643
|
||||||
|
AddFriends(userId string, isBothAdd, isForceAdd bool, friends ...*Friend) (results []*Result, err error)
|
||||||
|
|
||||||
|
// ImportFriend 导入单个好友
|
||||||
|
// 本方法拓展于“添加多个好友(ImportFriends)”方法。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/8301
|
||||||
|
ImportFriend(userId string, friend *Friend) (err error)
|
||||||
|
|
||||||
|
// ImportFriends 导入多个好友
|
||||||
|
// 支持批量导入单向好友。
|
||||||
|
// 往同一个用户导入好友时建议采用批量导入的方式,避免并发写导致的写冲突。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/8301
|
||||||
|
ImportFriends(userId string, friends ...*Friend) (results []*Result, err error)
|
||||||
|
|
||||||
|
// UpdateFriend 更新单个好友
|
||||||
|
// 本方法拓展于“更新多个好友(UpdateFriends)”方法。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/12525
|
||||||
|
UpdateFriend(userId string, friend *Friend) (err error)
|
||||||
|
|
||||||
|
// UpdateFriends 更新多个好友
|
||||||
|
// 支持批量更新同一用户的多个好友的关系链数据。
|
||||||
|
// 更新一个用户多个好友时,建议采用批量方式,避免并发写导致的写冲突。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/12525
|
||||||
|
UpdateFriends(userId string, friends ...*Friend) (results []*Result, err error)
|
||||||
|
|
||||||
|
// DeleteFriend 删除单个好友
|
||||||
|
// 本方法拓展于“删除多个好友(DeleteFriends)”方法。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/1644
|
||||||
|
DeleteFriend(userId string, isBothDelete bool, deletedUserId string) (err error)
|
||||||
|
|
||||||
|
// DeleteFriends 删除多个好友
|
||||||
|
// 删除好友,支持单向删除好友和双向删除好友。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/1644
|
||||||
|
DeleteFriends(userId string, isBothDelete bool, deletedUserIds ...string) (results []*Result, err error)
|
||||||
|
|
||||||
|
// DeleteAllFriends 删除所有好友
|
||||||
|
// 清除指定用户的标配好友数据和自定义好友数据。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/1645
|
||||||
|
DeleteAllFriends(userId string, deleteType ...DeleteType) (err error)
|
||||||
|
|
||||||
|
// CheckFriend 校验单个好友
|
||||||
|
// 本方法拓展于“校验多个好友(CheckFriends)”方法。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/1646
|
||||||
|
CheckFriend(userId string, checkType CheckType, checkedUserId string) (relation string, err error)
|
||||||
|
|
||||||
|
// CheckFriends 校验多个好友
|
||||||
|
// 支持批量校验好友关系。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/1646
|
||||||
|
CheckFriends(userId string, checkType CheckType, checkedUserIds ...string) (results []*CheckResult, err error)
|
||||||
|
|
||||||
|
// GetFriend 拉取单个指定好友
|
||||||
|
// 本方法拓展于“拉取多个指定好友(GetFriends)”方法。
|
||||||
|
// 支持拉取指定好友的好友数据和资料数据。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/8609
|
||||||
|
GetFriend(userId string, tagList []string, friendUserId string) (friend *Friend, err error)
|
||||||
|
|
||||||
|
// GetFriends 拉取多个指定好友
|
||||||
|
// 支持拉取指定好友的好友数据和资料数据。
|
||||||
|
// 建议每次拉取的好友数不超过100,避免因数据量太大导致回包失败。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/8609
|
||||||
|
GetFriends(userId string, tagList []string, friendUserIds ...string) (friends []*Friend, err error)
|
||||||
|
|
||||||
|
// FetchFriends 拉取好友
|
||||||
|
// 分页拉取全量好友数据。
|
||||||
|
// 不支持资料数据的拉取。
|
||||||
|
// 不需要指定请求拉取的字段,默认返回全量的标配好友数据和自定义好友数据。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/1647
|
||||||
|
FetchFriends(userId string, startIndex int, sequence ...int) (ret *FetchFriendsRet, err error)
|
||||||
|
|
||||||
|
// PullFriends 续拉取好友
|
||||||
|
// 本API是借助"拉取好友"API进行扩展实现
|
||||||
|
// 分页拉取全量好友数据。
|
||||||
|
// 不支持资料数据的拉取。
|
||||||
|
// 不需要指定请求拉取的字段,默认返回全量的标配好友数据和自定义好友数据。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/1647
|
||||||
|
PullFriends(userId string, fn func(ret *FetchFriendsRet)) (err error)
|
||||||
|
|
||||||
|
// AddBlacklist 添加黑名单
|
||||||
|
// 添加黑名单,支持批量添加黑名单。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/3718
|
||||||
|
AddBlacklist(userId string, blackedUserIds ...string) (results []*Result, err error)
|
||||||
|
|
||||||
|
// DeleteBlacklist 删除黑名单
|
||||||
|
// 删除指定黑名单。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/3719
|
||||||
|
DeleteBlacklist(userId string, deletedUserIds ...string) (results []*Result, err error)
|
||||||
|
|
||||||
|
// FetchBlacklist 拉取黑名单
|
||||||
|
// 支持分页拉取所有黑名单。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/3722
|
||||||
|
FetchBlacklist(userId string, maxLimited int, startIndexAndSequence ...int) (ret *FetchBlacklistRet, err error)
|
||||||
|
|
||||||
|
// PullBlacklist 拉取黑名单
|
||||||
|
// 本API是借助"拉取黑名单"API进行扩展实现
|
||||||
|
// 支持分页拉取所有黑名单。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/3722
|
||||||
|
PullBlacklist(userId string, maxLimited int, fn func(ret *FetchBlacklistRet)) (err error)
|
||||||
|
|
||||||
|
// CheckBlacklist 校验黑名单
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/3725
|
||||||
|
CheckBlacklist(userId string, checkType BlacklistCheckType, checkedUserIds ...string) (results []*CheckResult, err error)
|
||||||
|
|
||||||
|
// AddGroups 添加分组
|
||||||
|
// 添加分组,支持批量添加分组,并将指定好友加入到新增分组中。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/10107
|
||||||
|
AddGroups(userId string, groupNames []string, joinedUserIds ...[]string) (currentSequence int, results []*Result, err error)
|
||||||
|
|
||||||
|
// DeleteGroups 删除分组
|
||||||
|
// 删除指定分组。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/10108
|
||||||
|
DeleteGroups(userId string, groupNames ...string) (currentSequence int, err error)
|
||||||
|
|
||||||
|
// GetGroups 拉取分组
|
||||||
|
// 拉取分组,支持指定分组以及拉取分组下的好友列表。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/54763
|
||||||
|
GetGroups(userId string, lastSequence int, isGetFriends bool, groupNames ...string) (currentSequence int, results []*GroupResult, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type api struct {
|
||||||
|
client core.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAPI(client core.Client) API {
|
||||||
|
return &api{client: client}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddFriend 添加单个好友
|
||||||
|
// 本方法拓展于“添加多个好友(AddFriends)”方法。
|
||||||
|
// 添加好友,仅支持添加单个好友
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/1643
|
||||||
|
func (a *api) AddFriend(userId string, isBothAdd, isForceAdd bool, friend *Friend) (err error) {
|
||||||
|
var results []*Result
|
||||||
|
|
||||||
|
if results, err = a.AddFriends(userId, isBothAdd, isForceAdd, friend); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, result := range results {
|
||||||
|
if result.UserId == friend.GetUserId() && result.ResultCode != enum.SuccessCode {
|
||||||
|
return core.NewError(result.ResultCode, result.ResultInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddFriends 添加多个好友
|
||||||
|
// 添加好友,支持批量添加好友
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/1643
|
||||||
|
func (a *api) AddFriends(userId string, isBothAdd, isForceAdd bool, friends ...*Friend) (results []*Result, err error) {
|
||||||
|
if len(friends) == 0 {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, "the friends is not set")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &addFriendsReq{UserId: userId, Friends: make([]*addFriendItem, 0, len(friends))}
|
||||||
|
|
||||||
|
for _, friend := range friends {
|
||||||
|
if err = friend.checkError(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
item := new(addFriendItem)
|
||||||
|
item.UserId = friend.GetUserId()
|
||||||
|
item.Remark, _ = friend.GetRemark()
|
||||||
|
item.AddWording, _ = friend.GetAddWording()
|
||||||
|
item.AddSource, _ = friend.GetSrcAddSource()
|
||||||
|
if groups, exist := friend.GetGroup(); exist {
|
||||||
|
item.GroupName = groups[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Friends = append(req.Friends, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
if isBothAdd {
|
||||||
|
req.AddType = AddTypeBoth
|
||||||
|
} else {
|
||||||
|
req.AddType = AddTypeSingle
|
||||||
|
}
|
||||||
|
|
||||||
|
if isForceAdd {
|
||||||
|
req.ForceAddFlags = ForceAddYes
|
||||||
|
} else {
|
||||||
|
req.ForceAddFlags = ForceAddNo
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := &addFriendsResp{}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandAddFriend, req, resp); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
results = resp.Results
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImportFriend 导入单个好友
|
||||||
|
// 本方法拓展于“添加多个好友(ImportFriends)”方法。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/8301
|
||||||
|
func (a *api) ImportFriend(userId string, friend *Friend) (err error) {
|
||||||
|
var results []*Result
|
||||||
|
|
||||||
|
if results, err = a.ImportFriends(userId, friend); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, result := range results {
|
||||||
|
if result.UserId == friend.GetUserId() && result.ResultCode != enum.SuccessCode {
|
||||||
|
return core.NewError(result.ResultCode, result.ResultInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImportFriends 导入多个好友
|
||||||
|
// 支持批量导入单向好友。
|
||||||
|
// 往同一个用户导入好友时建议采用批量导入的方式,避免并发写导致的写冲突。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/8301
|
||||||
|
func (a *api) ImportFriends(userId string, friends ...*Friend) (results []*Result, err error) {
|
||||||
|
if len(friends) == 0 {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, "the friends is not set")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &importFriendsReq{UserId: userId, Friends: make([]*importFriendItem, 0, len(friends))}
|
||||||
|
|
||||||
|
for _, friend := range friends {
|
||||||
|
if err = friend.checkError(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
item := new(importFriendItem)
|
||||||
|
item.UserId = friend.GetUserId()
|
||||||
|
item.Remark, _ = friend.GetRemark()
|
||||||
|
item.AddWording, _ = friend.GetAddWording()
|
||||||
|
item.AddTime, _ = friend.GetAddTime()
|
||||||
|
item.RemarkTime, _ = friend.GetRemarkTime()
|
||||||
|
item.AddSource, _ = friend.GetSrcAddSource()
|
||||||
|
item.GroupName, _ = friend.GetGroup()
|
||||||
|
|
||||||
|
if customAttrs := friend.GetSNSCustomAttrs(); len(customAttrs) > 0 {
|
||||||
|
item.CustomData = make([]*types.TagPair, 0, len(customAttrs))
|
||||||
|
for k, v := range customAttrs {
|
||||||
|
item.CustomData = append(item.CustomData, &types.TagPair{
|
||||||
|
Tag: k,
|
||||||
|
Value: v,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Friends = append(req.Friends, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := &importFriendsResp{}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandImportFriend, req, resp); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
results = resp.Results
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateFriend 更新单个好友
|
||||||
|
// 本方法拓展于“更新多个好友(UpdateFriends)”方法。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/12525
|
||||||
|
func (a *api) UpdateFriend(userId string, friend *Friend) (err error) {
|
||||||
|
var results []*Result
|
||||||
|
|
||||||
|
if results, err = a.UpdateFriends(userId, friend); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, result := range results {
|
||||||
|
if result.UserId == friend.GetUserId() && result.ResultCode != enum.SuccessCode {
|
||||||
|
return core.NewError(result.ResultCode, result.ResultInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateFriends 更新多个好友
|
||||||
|
// 支持批量更新同一用户的多个好友的关系链数据。
|
||||||
|
// 更新一个用户多个好友时,建议采用批量方式,避免并发写导致的写冲突。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/12525
|
||||||
|
func (a *api) UpdateFriends(userId string, friends ...*Friend) (results []*Result, err error) {
|
||||||
|
if len(friends) == 0 {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, "the friends is not set")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &updateFriendsReq{UserId: userId, Friends: make([]*updateFriendItem, 0, len(friends))}
|
||||||
|
|
||||||
|
for _, friend := range friends {
|
||||||
|
item := new(updateFriendItem)
|
||||||
|
item.UserId = friend.GetUserId()
|
||||||
|
|
||||||
|
for k, v := range friend.GetSNSAttrs() {
|
||||||
|
switch k {
|
||||||
|
case FriendAttrAddSource, FriendAttrAddTime, FriendAttrRemarkTime, FriendAttrAddWording:
|
||||||
|
default:
|
||||||
|
item.Attrs = append(item.Attrs, &types.TagPair{
|
||||||
|
Tag: k,
|
||||||
|
Value: v,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range friend.GetSNSCustomAttrs() {
|
||||||
|
item.Attrs = append(item.Attrs, &types.TagPair{
|
||||||
|
Tag: k,
|
||||||
|
Value: v,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Friends = append(req.Friends, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := &updateFriendsResp{}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandUpdateFriend, req, resp); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
results = resp.Results
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteFriend 删除单个好友
|
||||||
|
// 本方法拓展于“删除多个好友(DeleteFriends)”方法。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/1644
|
||||||
|
func (a *api) DeleteFriend(userId string, isBothDelete bool, deletedUserId string) (err error) {
|
||||||
|
var results []*Result
|
||||||
|
|
||||||
|
if results, err = a.DeleteFriends(userId, isBothDelete, deletedUserId); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if results != nil && len(results) > 0 {
|
||||||
|
for _, result := range results {
|
||||||
|
if result.UserId == deletedUserId && result.ResultCode != enum.SuccessCode {
|
||||||
|
return core.NewError(result.ResultCode, result.ResultInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteFriends 删除多个好友
|
||||||
|
// 删除好友,支持单向删除好友和双向删除好友。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/1644
|
||||||
|
func (a *api) DeleteFriends(userId string, isBothDelete bool, deletedUserIds ...string) (results []*Result, err error) {
|
||||||
|
req := &deleteFriendsReq{UserId: userId, DeletedUserIds: deletedUserIds}
|
||||||
|
resp := &deleteFriendsResp{}
|
||||||
|
|
||||||
|
if isBothDelete {
|
||||||
|
req.DeleteType = DeleteTypeBoth
|
||||||
|
} else {
|
||||||
|
req.DeleteType = DeleteTypeSingle
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandDeleteFriend, req, resp); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
results = resp.Results
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteAllFriends 删除所有好友
|
||||||
|
// 清除指定用户的标配好友数据和自定义好友数据。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/1645
|
||||||
|
func (a *api) DeleteAllFriends(userId string, deleteType ...DeleteType) (err error) {
|
||||||
|
req := &deleteAllFriendsReq{UserId: userId}
|
||||||
|
|
||||||
|
if len(deleteType) > 0 {
|
||||||
|
req.DeleteType = deleteType[0]
|
||||||
|
} else {
|
||||||
|
req.DeleteType = DeleteTypeSingle
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandDeleteAllFriend, req, &types.ActionBaseResp{}); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckFriend 校验单个好友
|
||||||
|
// 本方法拓展于“校验多个好友(CheckFriends)”方法。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/1646
|
||||||
|
func (a *api) CheckFriend(userId string, checkType CheckType, checkedUserId string) (relation string, err error) {
|
||||||
|
var results []*CheckResult
|
||||||
|
|
||||||
|
if results, err = a.CheckFriends(userId, checkType, checkedUserId); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if results != nil && len(results) > 0 {
|
||||||
|
for _, result := range results {
|
||||||
|
if result.UserId == checkedUserId {
|
||||||
|
if result.ResultCode != enum.SuccessCode {
|
||||||
|
err = core.NewError(result.ResultCode, result.ResultInfo)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
relation = result.Relation
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckFriends 校验多个好友
|
||||||
|
// 支持批量校验好友关系。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/1646
|
||||||
|
func (a *api) CheckFriends(userId string, checkType CheckType, checkedUserIds ...string) (results []*CheckResult, err error) {
|
||||||
|
if c := len(checkedUserIds); c == 0 {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, "the accounts is not set")
|
||||||
|
return
|
||||||
|
} else if c > batchCheckFriendsLimit {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, fmt.Sprintf("the number of checked accounts cannot exceed %d", batchCheckFriendsLimit))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &checkFriendsReq{UserId: userId, CheckedUserIds: checkedUserIds, CheckType: checkType}
|
||||||
|
resp := &checkFriendsResp{}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandCheckFriend, req, resp); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
results = resp.Results
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFriend 拉取单个指定好友
|
||||||
|
// 本方法拓展于“拉取多个指定好友(GetFriends)”方法。
|
||||||
|
// 支持拉取指定好友的好友数据和资料数据。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/8609
|
||||||
|
func (a *api) GetFriend(userId string, tagList []string, friendUserId string) (friend *Friend, err error) {
|
||||||
|
var friends []*Friend
|
||||||
|
|
||||||
|
if friends, err = a.GetFriends(userId, tagList, friendUserId); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(friends) > 0 {
|
||||||
|
friend = friends[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFriends 拉取多个指定好友
|
||||||
|
// 支持拉取指定好友的好友数据和资料数据。
|
||||||
|
// 建议每次拉取的好友数不超过100,避免因数据量太大导致回包失败。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/8609
|
||||||
|
func (a *api) GetFriends(userId string, tagList []string, friendUserIds ...string) (friends []*Friend, err error) {
|
||||||
|
if c := len(friendUserIds); c == 0 {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, "the account of friends is not set")
|
||||||
|
return
|
||||||
|
} else if c > batchGetFriendsLimit {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, fmt.Sprintf("the number of friend's account cannot exceed %d", batchGetFriendsLimit))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &getFriendsReq{UserId: userId, FriendUserIds: friendUserIds}
|
||||||
|
resp := &getFriendsResp{}
|
||||||
|
|
||||||
|
for _, tag := range tagList {
|
||||||
|
switch tag {
|
||||||
|
case FriendAttrRemarkTime:
|
||||||
|
default:
|
||||||
|
req.TagList = append(req.TagList, tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandGetFriend, req, resp); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
friends = make([]*Friend, 0, len(resp.Friends))
|
||||||
|
|
||||||
|
for _, item := range resp.Friends {
|
||||||
|
friend := NewFriend(item.UserId)
|
||||||
|
friend.SetError(item.ResultCode, item.ResultInfo)
|
||||||
|
for _, v := range item.Profiles {
|
||||||
|
friend.SetAttr(v.Tag, v.Value)
|
||||||
|
}
|
||||||
|
friends = append(friends, friend)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FetchFriends 拉取好友
|
||||||
|
// 分页拉取全量好友数据。
|
||||||
|
// 不支持资料数据的拉取。
|
||||||
|
// 不需要指定请求拉取的字段,默认返回全量的标配好友数据和自定义好友数据。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/1647
|
||||||
|
func (a *api) FetchFriends(userId string, startIndex int, sequence ...int) (ret *FetchFriendsRet, err error) {
|
||||||
|
req := &fetchFriendsReq{UserId: userId, StartIndex: startIndex}
|
||||||
|
resp := &fetchFriendsResp{}
|
||||||
|
|
||||||
|
if len(sequence) > 0 {
|
||||||
|
req.StandardSequence = sequence[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(sequence) > 1 {
|
||||||
|
req.CustomSequence = sequence[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandFetchFriend, req, resp); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = &FetchFriendsRet{
|
||||||
|
StandardSequence: resp.StandardSequence,
|
||||||
|
CustomSequence: resp.CustomSequence,
|
||||||
|
StartIndex: resp.NextStartIndex,
|
||||||
|
Total: resp.FriendNum,
|
||||||
|
HasMore: resp.CompleteFlag == 0,
|
||||||
|
List: make([]*Friend, 0, len(resp.Friends)),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range resp.Friends {
|
||||||
|
friend := NewFriend(item.UserId)
|
||||||
|
for _, v := range item.Values {
|
||||||
|
friend.SetAttr(v.Tag, v.Value)
|
||||||
|
}
|
||||||
|
ret.List = append(ret.List, friend)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// PullFriends 续拉取好友
|
||||||
|
// 本API是借助"拉取好友"API进行扩展实现
|
||||||
|
// 分页拉取全量好友数据。
|
||||||
|
// 不支持资料数据的拉取。
|
||||||
|
// 不需要指定请求拉取的字段,默认返回全量的标配好友数据和自定义好友数据。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/1647
|
||||||
|
func (a *api) PullFriends(userId string, fn func(ret *FetchFriendsRet)) (err error) {
|
||||||
|
var (
|
||||||
|
ret *FetchFriendsRet
|
||||||
|
startIndex int
|
||||||
|
standardSequence int
|
||||||
|
customSequence int
|
||||||
|
)
|
||||||
|
|
||||||
|
for ret == nil || ret.HasMore {
|
||||||
|
ret, err = a.FetchFriends(userId, startIndex, standardSequence, customSequence)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fn(ret)
|
||||||
|
|
||||||
|
if ret.HasMore {
|
||||||
|
startIndex = ret.StartIndex
|
||||||
|
standardSequence = ret.StandardSequence
|
||||||
|
customSequence = ret.CustomSequence
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddBlacklist 添加黑名单
|
||||||
|
// 添加黑名单,支持批量添加黑名单。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/3718
|
||||||
|
func (a *api) AddBlacklist(userId string, blackedUserIds ...string) (results []*Result, err error) {
|
||||||
|
if c := len(blackedUserIds); c == 0 {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, "the blacked accounts is not set")
|
||||||
|
return
|
||||||
|
} else if c > batchAddBlacklistLimit {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, fmt.Sprintf("the number of blacked accounts cannot exceed %d", batchAddBlacklistLimit))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &addBlacklistReq{UserId: userId, BlackedUserIds: blackedUserIds}
|
||||||
|
resp := &addBlacklistResp{}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandAddBlackList, req, resp); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
results = resp.Results
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteBlacklist 删除黑名单
|
||||||
|
// 删除指定黑名单。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/3719
|
||||||
|
func (a *api) DeleteBlacklist(userId string, deletedUserIds ...string) (results []*Result, err error) {
|
||||||
|
if c := len(deletedUserIds); c == 0 {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, "the deleted accounts is not set")
|
||||||
|
return
|
||||||
|
} else if c > batchDeleteBlacklistLimit {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, fmt.Sprintf("the number of deleted accounts cannot exceed %d", batchDeleteBlacklistLimit))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &deleteBlacklistReq{UserId: userId, DeletedUserIds: deletedUserIds}
|
||||||
|
resp := &deleteBlacklistResp{}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandDeleteBlackList, req, resp); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
results = resp.Results
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FetchBlacklist 拉取黑名单
|
||||||
|
// 支持分页拉取所有黑名单。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/3722
|
||||||
|
func (a *api) FetchBlacklist(userId string, maxLimited int, startIndexAndSequence ...int) (ret *FetchBlacklistRet, err error) {
|
||||||
|
req := &fetchBlacklistReq{UserId: userId, MaxLimited: maxLimited}
|
||||||
|
|
||||||
|
if len(startIndexAndSequence) > 0 {
|
||||||
|
req.StartIndex = startIndexAndSequence[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(startIndexAndSequence) > 1 {
|
||||||
|
req.LastSequence = startIndexAndSequence[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := &fetchBlacklistResp{}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandGetBlackList, req, resp); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = &FetchBlacklistRet{
|
||||||
|
StartIndex: resp.StartIndex,
|
||||||
|
StandardSequence: resp.CurrentSequence,
|
||||||
|
List: resp.Blacklists,
|
||||||
|
HasMore: resp.StartIndex != 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// PullBlacklist 拉取黑名单
|
||||||
|
// 本API是借助"拉取黑名单"API进行扩展实现
|
||||||
|
// 支持分页拉取所有黑名单。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/3722
|
||||||
|
func (a *api) PullBlacklist(userId string, maxLimited int, fn func(ret *FetchBlacklistRet)) (err error) {
|
||||||
|
var (
|
||||||
|
ret *FetchBlacklistRet
|
||||||
|
startIndex = 0
|
||||||
|
standardSequence = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
for ret == nil || ret.HasMore {
|
||||||
|
ret, err = a.FetchBlacklist(userId, maxLimited, startIndex, standardSequence)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fn(ret)
|
||||||
|
|
||||||
|
if ret.HasMore {
|
||||||
|
startIndex = ret.StartIndex
|
||||||
|
standardSequence = ret.StandardSequence
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckBlacklist 校验黑名单
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/3725
|
||||||
|
func (a *api) CheckBlacklist(userId string, checkType BlacklistCheckType, checkedUserIds ...string) (results []*CheckResult, err error) {
|
||||||
|
if c := len(checkedUserIds); c == 0 {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, "the checked accounts is not set")
|
||||||
|
return
|
||||||
|
} else if c > batchCheckBlacklistLimit {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, fmt.Sprintf("the number of checked accounts cannot exceed %d", batchCheckBlacklistLimit))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &checkBlacklistReq{UserId: userId, CheckedUserIds: checkedUserIds, CheckType: checkType}
|
||||||
|
resp := &checkBlacklistResp{}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandCheckBlackList, req, resp); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
results = resp.Results
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddGroups 添加分组
|
||||||
|
// 添加分组,支持批量添加分组,并将指定好友加入到新增分组中。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/10107
|
||||||
|
func (a *api) AddGroups(userId string, groupNames []string, joinedUserIds ...[]string) (currentSequence int, results []*Result, err error) {
|
||||||
|
if c := len(groupNames); c == 0 {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, "the added groups is not set")
|
||||||
|
return
|
||||||
|
} else if c > batchAddGroupsLimit {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, fmt.Sprintf("the number of added groups cannot exceed %d", batchAddGroupsLimit))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &addGroupsReq{UserId: userId, GroupNames: groupNames}
|
||||||
|
|
||||||
|
if len(joinedUserIds) > 0 {
|
||||||
|
if c := len(joinedUserIds[0]); c == 0 {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, "the added groups is not set")
|
||||||
|
return
|
||||||
|
} else if c > batchJoinGroupsLimit {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, fmt.Sprintf("the number of accounts joining the group cannot exceed %d", batchJoinGroupsLimit))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req.JoinedUserIds = joinedUserIds[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := &addGroupsResp{}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandAddGroup, req, resp); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
currentSequence = resp.CurrentSequence
|
||||||
|
results = resp.Results
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteGroups 删除分组
|
||||||
|
// 删除指定分组。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/10108
|
||||||
|
func (a *api) DeleteGroups(userId string, groupNames ...string) (currentSequence int, err error) {
|
||||||
|
if c := len(groupNames); c == 0 {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, "the deleted groups is not set")
|
||||||
|
return
|
||||||
|
} else if c > batchDeleteGroupsLimit {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, fmt.Sprintf("the number of deleted groups cannot exceed %d", batchDeleteGroupsLimit))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &deleteGroupsReq{UserId: userId, GroupNames: groupNames}
|
||||||
|
resp := &deleteGroupsResp{}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandDeleteGroup, req, resp); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
currentSequence = resp.CurrentSequence
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGroups 拉取分组
|
||||||
|
// 拉取分组,支持指定分组以及拉取分组下的好友列表。
|
||||||
|
// 点击查看详细文档:
|
||||||
|
// https://cloud.tencent.com/document/product/269/54763
|
||||||
|
func (a *api) GetGroups(userId string, lastSequence int, isGetFriends bool, groupNames ...string) (currentSequence int, results []*GroupResult, err error) {
|
||||||
|
if c := len(groupNames); c == 0 {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, "the gotten groups is not set")
|
||||||
|
return
|
||||||
|
} else if c > batchGetGroupsLimit {
|
||||||
|
err = core.NewError(enum.InvalidParamsCode, fmt.Sprintf("the number of gotten groups cannot exceed %d", batchGetGroupsLimit))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &getGroupsReq{UserId: userId, LastSequence: lastSequence, GroupNames: groupNames}
|
||||||
|
resp := &getGroupsResp{}
|
||||||
|
|
||||||
|
if isGetFriends {
|
||||||
|
req.NeedFriend = NeedFriendYes
|
||||||
|
} else {
|
||||||
|
req.NeedFriend = NeedFriendNo
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = a.client.Post(service, commandGetGroup, req, resp); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
currentSequence = resp.CurrentSequence
|
||||||
|
results = resp.Results
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
90
sns/enum.go
Normal file
90
sns/enum.go
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Author:1711788888@qq.com
|
||||||
|
* @Date: 2021/9/7 14:40
|
||||||
|
* @Desc: TODO
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sns
|
||||||
|
|
||||||
|
import "git.echol.cn/loser/tencent-im/internal/enum"
|
||||||
|
|
||||||
|
type (
|
||||||
|
// AddType 添加类型
|
||||||
|
AddType string
|
||||||
|
|
||||||
|
// DeleteType 删除类型
|
||||||
|
DeleteType string
|
||||||
|
|
||||||
|
// CheckType 校验模式
|
||||||
|
CheckType string
|
||||||
|
|
||||||
|
// ForceAddType 强制添加类型
|
||||||
|
ForceAddType int
|
||||||
|
|
||||||
|
// BlacklistCheckType 黑名单校验模式
|
||||||
|
BlacklistCheckType string
|
||||||
|
|
||||||
|
// NeedFriendType 是否需要拉取分组下的User列表
|
||||||
|
NeedFriendType string
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// 标准资料字段
|
||||||
|
StandardAttrNickname = enum.StandardAttrNickname // 昵称
|
||||||
|
StandardAttrGender = enum.StandardAttrGender // 性别
|
||||||
|
StandardAttrBirthday = enum.StandardAttrBirthday // 生日
|
||||||
|
StandardAttrLocation = enum.StandardAttrLocation // 所在地
|
||||||
|
StandardAttrSignature = enum.StandardAttrSignature // 个性签名
|
||||||
|
StandardAttrAllowType = enum.StandardAttrAllowType // 加好友验证方式
|
||||||
|
StandardAttrLanguage = enum.StandardAttrLanguage // 语言
|
||||||
|
StandardAttrAvatar = enum.StandardAttrAvatar // 头像URL
|
||||||
|
StandardAttrMsgSettings = enum.StandardAttrMsgSettings // 消息设置
|
||||||
|
StandardAttrAdminForbidType = enum.StandardAttrAdminForbidType // 管理员禁止加好友标识
|
||||||
|
StandardAttrLevel = enum.StandardAttrLevel // 等级
|
||||||
|
StandardAttrRole = enum.StandardAttrRole // 角色
|
||||||
|
|
||||||
|
// 好友属性
|
||||||
|
FriendAttrAddSource = "Tag_SNS_IM_AddSource" // 添加源
|
||||||
|
FriendAttrRemark = "Tag_SNS_IM_Remark" // 备注
|
||||||
|
FriendAttrGroup = "Tag_SNS_IM_Group" // 分组
|
||||||
|
FriendAttrAddWording = "Tag_SNS_IM_AddWording" // 附言信息
|
||||||
|
FriendAttrAddTime = "Tag_SNS_IM_AddTime" // 添加时间
|
||||||
|
FriendAttrRemarkTime = "Tag_SNS_IM_RemarkTime" // 备注时间
|
||||||
|
|
||||||
|
// 添加类型
|
||||||
|
AddTypeSingle AddType = "Add_Type_Single" // 单向添加
|
||||||
|
AddTypeBoth AddType = "Add_Type_Both" // 双向添加
|
||||||
|
|
||||||
|
// 删除类型
|
||||||
|
DeleteTypeSingle DeleteType = "Delete_Type_Single" // 单向删除
|
||||||
|
DeleteTypeBoth DeleteType = "Delete_Type_Both" // 双向删除
|
||||||
|
|
||||||
|
// 校验模式
|
||||||
|
CheckTypeSingle CheckType = "CheckResult_Type_Single" // 单向校验好友关系
|
||||||
|
CheckTypeBoth CheckType = "CheckResult_Type_Both" // 双向校验好友关系
|
||||||
|
|
||||||
|
// 黑名单校验模式
|
||||||
|
BlacklistCheckTypeSingle BlacklistCheckType = "BlackCheckResult_Type_Single" // 单向校验黑名单关系
|
||||||
|
BlacklistCheckTypeBoth BlacklistCheckType = "BlackCheckResult_Type_Both" // 双向校验黑名单关系
|
||||||
|
|
||||||
|
// 强制加好友类型
|
||||||
|
ForceAddYes ForceAddType = 1 // 强制加好友
|
||||||
|
ForceAddNo ForceAddType = 0 // 常规加好友
|
||||||
|
|
||||||
|
// 是否需要拉取分组下的 User 列表
|
||||||
|
NeedFriendYes NeedFriendType = "Need_Friend_Type_Yes" // 需要拉取
|
||||||
|
NeedFriendNo NeedFriendType = "Need_Friend_Type_No" // 不需要拉取
|
||||||
|
|
||||||
|
// 好友关系结果
|
||||||
|
CheckResultTypeNoRelation = "CheckResult_Type_NoRelation" // From_Account 的好友表中没有 To_Account,但无法确定 To_Account 的好友表中是否有 From_Account
|
||||||
|
CheckResultTypeAWithB = "CheckResult_Type_AWithB" // From_Account 的好友表中有 To_Account,但无法确定 To_Account 的好友表中是否有 From_Account
|
||||||
|
CheckResultTypeBWithA = "CheckResult_Type_BWithA" // From_Account 的好友表中没有 To_Account,但 To_Account 的好友表中有 From_Account
|
||||||
|
CheckResultTypeBothWay = "CheckResult_Type_BothWay" // From_Account 的好友表中有 To_Account,To_Account 的好友表中也有 From_Account
|
||||||
|
|
||||||
|
// 黑名单关系结果
|
||||||
|
BlackCheckResultTypeNO = "BlackCheckResult_Type_NO" // From_Account 的黑名单中没有 To_Account,但无法确定 To_Account 的黑名单中是否有 From_Account
|
||||||
|
BlackCheckResultTypeAWithB = "BlackCheckResult_Type_AWithB" // From_Account 的黑名单中有 To_Account,但无法确定 To_Account 的黑名单中是否有 From_Account
|
||||||
|
BlackCheckResultTypeBWithA = "BlackCheckResult_Type_BWithA" // From_Account 的黑名单中没有 To_Account,但 To_Account 的黑名单中有 From_Account
|
||||||
|
BlackCheckResultTypeBothWay = "BlackCheckResult_Type_BothWay" // From_Account 的黑名单中有 To_Account,To_Account 的黑名单中也有 From_Account
|
||||||
|
)
|
206
sns/friend.go
Normal file
206
sns/friend.go
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Email:1711788888@qq.com
|
||||||
|
* @Date: 2021/8/30 2:55 下午
|
||||||
|
* @Desc: 好友关系
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.echol.cn/loser/tencent-im/internal/entity"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errNotSetAccount = errors.New("the friend's account is not set")
|
||||||
|
errNotSetAddSource = errors.New("the friend's add source is not set")
|
||||||
|
)
|
||||||
|
|
||||||
|
type Friend struct {
|
||||||
|
entity.User
|
||||||
|
customAttrs map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFriend(userId ...string) *Friend {
|
||||||
|
f := new(Friend)
|
||||||
|
f.customAttrs = make(map[string]interface{})
|
||||||
|
|
||||||
|
if len(userId) > 0 {
|
||||||
|
f.SetUserId(userId[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAddSource 设置添加来源
|
||||||
|
func (f *Friend) SetAddSource(addSource string) {
|
||||||
|
f.SetAttr(FriendAttrAddSource, "AddSource_Type_"+addSource)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAddSource 获取添加来源
|
||||||
|
func (f *Friend) GetAddSource() (addSource string, exist bool) {
|
||||||
|
var v interface{}
|
||||||
|
if v, exist = f.GetAttr(FriendAttrAddSource); exist {
|
||||||
|
addSource = strings.TrimLeft(v.(string), "AddSource_Type_")
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSrcAddSource 获取添加来源(原始的)
|
||||||
|
func (f *Friend) GetSrcAddSource() (addSource string, exist bool) {
|
||||||
|
var v interface{}
|
||||||
|
if v, exist = f.GetAttr(FriendAttrAddSource); exist {
|
||||||
|
addSource = v.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRemark 设置备注
|
||||||
|
func (f *Friend) SetRemark(remark string) {
|
||||||
|
f.SetAttr(FriendAttrRemark, remark)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRemark 获取备注
|
||||||
|
func (f *Friend) GetRemark() (remark string, exist bool) {
|
||||||
|
var v interface{}
|
||||||
|
if v, exist = f.GetAttr(FriendAttrRemark); exist {
|
||||||
|
remark = v.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetGroup 设置分组
|
||||||
|
func (f *Friend) SetGroup(groupName ...string) {
|
||||||
|
f.SetAttr(FriendAttrGroup, groupName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGroup 获取分组
|
||||||
|
func (f *Friend) GetGroup() (groups []string, exist bool) {
|
||||||
|
var v interface{}
|
||||||
|
if v, exist = f.GetAttr(FriendAttrGroup); exist && v != nil {
|
||||||
|
if vv, ok := v.([]interface{}); ok {
|
||||||
|
for _, group := range vv {
|
||||||
|
groups = append(groups, group.(string))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAddWording 设置形成好友关系时的附言信息
|
||||||
|
func (f *Friend) SetAddWording(addWording string) {
|
||||||
|
f.SetAttr(FriendAttrAddWording, addWording)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAddWording 获取形成好友关系时的附言信息
|
||||||
|
func (f *Friend) GetAddWording() (addWording string, exist bool) {
|
||||||
|
var v interface{}
|
||||||
|
if v, exist = f.GetAttr(FriendAttrAddWording); exist {
|
||||||
|
addWording = v.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAddTime 设置添加时间(忽略)
|
||||||
|
func (f *Friend) SetAddTime(addTime int64) {
|
||||||
|
f.SetAttr(FriendAttrAddTime, addTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAddTime 获取添加时间
|
||||||
|
func (f *Friend) GetAddTime() (addTime int64, exist bool) {
|
||||||
|
var v interface{}
|
||||||
|
if v, exist = f.GetAttr(FriendAttrAddTime); exist {
|
||||||
|
addTime = v.(int64)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRemarkTime 设置备注时间
|
||||||
|
func (f *Friend) SetRemarkTime(remarkTime int64) {
|
||||||
|
f.SetAttr(FriendAttrRemarkTime, remarkTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRemarkTime 获取备注时间
|
||||||
|
func (f *Friend) GetRemarkTime() (remarkTime int64, exist bool) {
|
||||||
|
var v interface{}
|
||||||
|
if v, exist = f.GetAttr(FriendAttrRemarkTime); exist {
|
||||||
|
remarkTime = v.(int64)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSNSCustomAttr 设置SNS自定义关系数据(自定义字段需要单独申请,请在 IM 控制台 >应用配置>功能配置申请自定义好友字段,申请提交后,自定义好友字段将在5分钟内生效)
|
||||||
|
func (f *Friend) SetSNSCustomAttr(name string, value interface{}) {
|
||||||
|
if f.customAttrs == nil {
|
||||||
|
f.customAttrs = make(map[string]interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
f.customAttrs[fmt.Sprintf("%s_%s", "Tag_SNS_Custom", name)] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSNSCustomAttr 设置SNS自定义关系数据 (自定义字段需要单独申请,请在 IM 控制台 >应用配置>功能配置申请自定义好友字段,申请提交后,自定义好友字段将在5分钟内生效)
|
||||||
|
func (f *Friend) GetSNSCustomAttr(name string) (value interface{}, exist bool) {
|
||||||
|
if f.customAttrs == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
value, exist = f.customAttrs[fmt.Sprintf("%s_%s", "Tag_SNS_Custom", name)]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSNSAttrs 获取SNS标准关系数据
|
||||||
|
func (f *Friend) GetSNSAttrs() (attrs map[string]interface{}) {
|
||||||
|
attrs = make(map[string]interface{})
|
||||||
|
|
||||||
|
for k, v := range f.GetAttrs() {
|
||||||
|
switch k {
|
||||||
|
case FriendAttrAddSource, FriendAttrRemark, FriendAttrGroup, FriendAttrAddWording, FriendAttrAddTime, FriendAttrRemarkTime:
|
||||||
|
attrs[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSNSCustomAttrs 获取SNS自定义关系数据(自定义字段需要单独申请,请在 IM 控制台 >应用配置>功能配置申请自定义好友字段,申请提交后,自定义好友字段将在5分钟内生效)
|
||||||
|
func (f *Friend) GetSNSCustomAttrs() (attrs map[string]interface{}) {
|
||||||
|
attrs = make(map[string]interface{})
|
||||||
|
|
||||||
|
if f.customAttrs == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range f.customAttrs {
|
||||||
|
switch k {
|
||||||
|
case FriendAttrAddSource, FriendAttrRemark, FriendAttrGroup, FriendAttrAddWording, FriendAttrAddTime, FriendAttrRemarkTime:
|
||||||
|
default:
|
||||||
|
attrs[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkError 检测参数错误
|
||||||
|
func (f *Friend) checkError() error {
|
||||||
|
if f.GetUserId() == "" {
|
||||||
|
return errNotSetAccount
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, exist := f.GetSrcAddSource(); !exist {
|
||||||
|
return errNotSetAddSource
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
301
sns/types.go
Normal file
301
sns/types.go
Normal file
@ -0,0 +1,301 @@
|
|||||||
|
/**
|
||||||
|
* @Author: Echo
|
||||||
|
* @Author:1711788888@qq.com
|
||||||
|
* @Date: 2021/5/29 17:34
|
||||||
|
* @Desc: 关系链管理数据类型
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sns
|
||||||
|
|
||||||
|
import "git.echol.cn/loser/tencent-im/internal/types"
|
||||||
|
|
||||||
|
type (
|
||||||
|
// 添加的好友信息
|
||||||
|
addFriendItem struct {
|
||||||
|
UserId string `json:"To_Account"` // (必填)好友的UserID
|
||||||
|
AddSource string `json:"AddSource"` // (必填)加好友来源
|
||||||
|
Remark string `json:"Remark,omitempty"` // (选填)加好友备注
|
||||||
|
GroupName string `json:"GroupName,omitempty"` // (选填)分组信息,添加好友时只允许设置一个分组,因此使用 String 类型即可
|
||||||
|
AddWording string `json:"AddWording,omitempty"` // (选填)好友关系时的附言信息
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加好友(请求)
|
||||||
|
addFriendsReq struct {
|
||||||
|
UserId string `json:"From_Account"` // (必填)需要为该UserID添加好友
|
||||||
|
Friends []*addFriendItem `json:"AddFriendItem"` // (必填)好友结构体对象
|
||||||
|
AddType AddType `json:"AddType"` // (选填)加好友方式(默认双向加好友方式)Add_Type_Single:表示单向加好友;Add_Type_Both:表示双向加好友
|
||||||
|
ForceAddFlags ForceAddType `json:"ForceAddFlags"` // (选填)管理员强制加好友标记:1表示强制加好友,0表示常规加好友方式
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加好友(响应)
|
||||||
|
addFriendsResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
Results []*Result `json:"ResultItem"` // 批量加好友的结果对象数组
|
||||||
|
}
|
||||||
|
|
||||||
|
// Result 添加结果
|
||||||
|
Result struct {
|
||||||
|
UserId string `json:"To_Account"` // 请求添加的好友的UserID
|
||||||
|
ResultCode int `json:"ResultCode"` // 处理结果,0表示成功,非0表示失败
|
||||||
|
ResultInfo string `json:"ResultInfo"` // 错误描述信息
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导入好友(请求)
|
||||||
|
importFriendsReq struct {
|
||||||
|
UserId string `json:"From_Account"` // 需要为该UserID添加好友
|
||||||
|
Friends []*importFriendItem `json:"AddFriendItem"` // 好友结构体对象
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导入好友(响应)
|
||||||
|
importFriendsResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
Results []*Result `json:"ResultItem"` // 结果对象数组
|
||||||
|
FailUserIds []string `json:"Fail_Account"` // 返回处理失败的用户列表,仅当存在失败用户时才返回该字段
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导入好友
|
||||||
|
importFriendItem struct {
|
||||||
|
UserId string `json:"To_Account"` // (必填)好友的UserID
|
||||||
|
AddSource string `json:"AddSource"` // (必填)加好友来源
|
||||||
|
Remark string `json:"Remark,omitempty"` // (选填)加好友备注
|
||||||
|
GroupName []string `json:"GroupName,omitempty"` // (选填)分组信息
|
||||||
|
AddWording string `json:"AddWording,omitempty"` // (选填)好友关系时的附言信息
|
||||||
|
AddTime int64 `json:"AddTime,omitempty"` // (选填)形成好友关系的时间
|
||||||
|
RemarkTime int64 `json:"RemarkTime,omitempty"` // (选填)好友备注时间
|
||||||
|
CustomData []*types.TagPair `json:"CustomItem,omitempty"` // (选填)自定义好友数据
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新好友(请求)
|
||||||
|
updateFriendsReq struct {
|
||||||
|
UserId string `json:"From_Account"` // (必填)需要更新该UserID的关系链数据
|
||||||
|
Friends []*updateFriendItem `json:"UpdateItem"` // (必填)需要更新的好友对象数组
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新好友(响应)
|
||||||
|
updateFriendsResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
Results []*Result `json:"ResultItem"` // 结果对象数组
|
||||||
|
FailUserIds []string `json:"Fail_Account"` // 返回处理失败的用户列表,仅当存在失败用户时才返回该字段
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新好友
|
||||||
|
updateFriendItem struct {
|
||||||
|
UserId string `json:"To_Account"` // (必填)好友的UserID
|
||||||
|
Attrs []*types.TagPair `json:"SnsItem"` // (必填)需要更新的关系链数据对象数组
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除好友(请求)
|
||||||
|
deleteFriendsReq struct {
|
||||||
|
UserId string `json:"From_Account"` // (必填)需要删除该UserID的好友
|
||||||
|
DeletedUserIds []string `json:"To_Account"` // (必填)待删除的好友的 UserID 列表,单次请求的 To_Account 数不得超过1000
|
||||||
|
DeleteType DeleteType `json:"DeleteType"` // (选填)删除模式
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除好友(响应)
|
||||||
|
deleteFriendsResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
ErrorDisplay string `json:"ErrorDisplay"` // 详细的客户端展示信息
|
||||||
|
Results []*Result `json:"ResultItem"` // 返回处理失败的用户列表,仅当存在失败用户时才返回该字段
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除所有好友(请求)
|
||||||
|
deleteAllFriendsReq struct {
|
||||||
|
UserId string `json:"From_Account"` // (必填)需要删除该UserID的好友
|
||||||
|
DeleteType DeleteType `json:"DeleteType"` // (选填)删除类型
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验好友(请求)
|
||||||
|
checkFriendsReq struct {
|
||||||
|
UserId string `json:"From_Account"` // (必填)需要校验该 UserID 的好友
|
||||||
|
CheckedUserIds []string `json:"To_Account"` // (必填)请求校验的好友的 UserID 列表,单次请求的 To_Account 数不得超过1000
|
||||||
|
CheckType CheckType `json:"CheckType"` // (必填)校验模式
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验好友(响应)
|
||||||
|
checkFriendsResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
Results []*CheckResult `json:"InfoItem"` // 结果对象数组
|
||||||
|
FailUserIds []string `json:"Fail_Account"` // 返回处理失败的用户列表,仅当存在失败用户时才返回该字段
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckResult 校验结果
|
||||||
|
CheckResult struct {
|
||||||
|
UserId string `json:"To_Account"` // 请求校验的用户的 UserID
|
||||||
|
Relation string `json:"Relation"` // 校验成功时 To_Account 与 From_Account 之间的好友关系
|
||||||
|
ResultCode int `json:"ResultCode"` // 处理结果,0表示成功,非0表示失败
|
||||||
|
ResultInfo string `json:"ResultInfo"` // 描述信息,成功时该字段为空
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拉取指定好友(请求)
|
||||||
|
getFriendsReq struct {
|
||||||
|
UserId string `json:"From_Account"` // (必填)指定要拉取好友数据的用户的 UserID
|
||||||
|
FriendUserIds []string `json:"To_Account"` // (必填)好友的 UserID 列表,建议每次请求的好友数不超过100,避免因数据量太大导致回包失败
|
||||||
|
TagList []string `json:"TagList"` // (必填)指定要拉取的资料字段及好友字段
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拉取指定好友(响应)
|
||||||
|
getFriendsResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
FailUserIds []string `json:"Fail_Account"` // 返回处理失败的用户列表,仅当存在失败用户时才返回该字段
|
||||||
|
Friends []friendData `json:"InfoItem"` // 好友对象数组
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拉取好友(请求)
|
||||||
|
fetchFriendsReq struct {
|
||||||
|
UserId string `json:"From_Account"` // (必填)需要拉取该 UserID 的黑名单
|
||||||
|
StartIndex int `json:"StartIndex"` // (必填)拉取的起始位置
|
||||||
|
StandardSequence int `json:"StandardSequence"` // (选填)上次拉好友数据时返回的 StandardSequence,如果 StandardSequence 字段的值与后台一致,后台不会返回标配好友数据
|
||||||
|
CustomSequence int `json:"CustomSequence"` // (选填)上次拉好友数据时返回的 CustomSequence,如果 CustomSequence 字段的值与后台一致,后台不会返回自定义好友数据
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拉取好友(响应)
|
||||||
|
fetchFriendsResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
Friends []friendData `json:"UserDataItem"` // 好友对象数组
|
||||||
|
StandardSequence int `json:"StandardSequence"` // 标配好友数据的 Sequence,客户端可以保存该 Sequence,下次请求时通过请求的 StandardSequence 字段返回给后台
|
||||||
|
CustomSequence int `json:"CustomSequence"` // 自定义好友数据的 Sequence,客户端可以保存该 Sequence,下次请求时通过请求的 CustomSequence 字段返回给后台
|
||||||
|
FriendNum int `json:"FriendNum"` // 好友总数
|
||||||
|
CompleteFlag int `json:"CompleteFlag"` // 分页的结束标识,非0值表示已完成全量拉取
|
||||||
|
NextStartIndex int `json:"NextStartIndex"` // 分页接口下一页的起始位置
|
||||||
|
}
|
||||||
|
|
||||||
|
// 好友信息
|
||||||
|
friendData struct {
|
||||||
|
UserId string `json:"To_Account"` // 好友的 UserID
|
||||||
|
Values []types.TagPair `json:"ValueItem"` // 好友数据的数组
|
||||||
|
Profiles []types.TagPair `json:"SnsProfileItem"` // 好友数据的数组
|
||||||
|
ResultCode int `json:"ResultCode"` // 处理结果,0表示成功,非0表示失败
|
||||||
|
ResultInfo string `json:"ResultInfo"` // 错误描述信息
|
||||||
|
}
|
||||||
|
|
||||||
|
// FetchFriendsRet 好友列表
|
||||||
|
FetchFriendsRet struct {
|
||||||
|
StandardSequence int // 标准排序
|
||||||
|
CustomSequence int // 自定义排序
|
||||||
|
StartIndex int // 分页接口下一页的起始位置
|
||||||
|
Total int // 好友总数
|
||||||
|
HasMore bool // 是否还有更多数据
|
||||||
|
List []*Friend // 好友列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加黑名单(请求)
|
||||||
|
addBlacklistReq struct {
|
||||||
|
UserId string `json:"From_Account"` // (必填)请求为该 UserID 添加黑名单
|
||||||
|
BlackedUserIds []string `json:"To_Account"` // (必填)待添加为黑名单的用户 UserID 列表,单次请求的 To_Account 数不得超过1000
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加黑名单(响应)
|
||||||
|
addBlacklistResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
FailUserIds []string `json:"Fail_Account"` // 返回处理失败的用户列表,仅当存在失败用户时才返回该字段
|
||||||
|
Results []*Result `json:"ResultItem"` // 批量添加黑名单的结果对象数组
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除黑名单(请求)
|
||||||
|
deleteBlacklistReq struct {
|
||||||
|
UserId string `json:"From_Account"` // (必填)需要删除该 UserID 的黑名单
|
||||||
|
DeletedUserIds []string `json:"To_Account"` // (必填)待删除的黑名单的 UserID 列表,单次请求的 To_Account 数不得超过1000
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除黑名单(响应)
|
||||||
|
deleteBlacklistResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
FailUserIds []string `json:"Fail_Account"` // 返回处理失败的用户列表,仅当存在失败用户时才返回该字段
|
||||||
|
Results []*Result `json:"ResultItem"` // 批量添加黑名单的结果对象数组
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拉取黑名单(请求)
|
||||||
|
fetchBlacklistReq struct {
|
||||||
|
UserId string `json:"From_Account"` // (必填)需要拉取该 UserID 的黑名单
|
||||||
|
StartIndex int `json:"StartIndex"` // (必填)拉取的起始位置
|
||||||
|
MaxLimited int `json:"MaxLimited"` // (必填)每页最多拉取的黑名单数
|
||||||
|
LastSequence int `json:"LastSequence"` // (必填)上一次拉黑名单时后台返回给客户端的 Seq,初次拉取时为0
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拉取黑名单(响应)
|
||||||
|
fetchBlacklistResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
ErrorDisplay string `json:"ErrorDisplay"` // 详细的客户端展示信息
|
||||||
|
StartIndex int `json:"StartIndex"` // 下页拉取的起始位置,0表示已拉完
|
||||||
|
CurrentSequence int `json:"CurrentSequence"` // 黑名单最新的 Seq
|
||||||
|
Blacklists []*Blacklist `json:"BlackListItem"` // 黑名单对象数组
|
||||||
|
}
|
||||||
|
|
||||||
|
// FetchBlacklistRet 拉取黑名单结果
|
||||||
|
FetchBlacklistRet struct {
|
||||||
|
StandardSequence int // 标准排序
|
||||||
|
StartIndex int // 分页接口下一页的起始位置
|
||||||
|
HasMore bool // 是否还有数据
|
||||||
|
List []*Blacklist // 黑名单列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blacklist 黑名单
|
||||||
|
Blacklist struct {
|
||||||
|
UserId string `json:"To_Account"` // 黑名单的 UserID
|
||||||
|
Time int `json:"AddBlackTimeStamp"` // 添加黑名单的时间
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验黑名单(请求)
|
||||||
|
checkBlacklistReq struct {
|
||||||
|
UserId string `json:"From_Account"` // (必填)需要校验该 UserID 的黑名单
|
||||||
|
CheckedUserIds []string `json:"To_Account"` // (必填)待校验的黑名单的 UserID 列表,单次请求的 To_Account 数不得超过1000
|
||||||
|
CheckType BlacklistCheckType `json:"CheckType"` // (必填)校验模式
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验黑名单(响应)
|
||||||
|
checkBlacklistResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
FailUserIds []string `json:"Fail_Account"` // 返回处理失败的用户列表,仅当存在失败用户时才返回该字段
|
||||||
|
Results []*CheckResult `json:"BlackListCheckItem"` // 校验结果对象数组
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加分组(请求)
|
||||||
|
addGroupsReq struct {
|
||||||
|
UserId string `json:"From_Account"` // (必填)需要为该 UserID 添加新分组
|
||||||
|
GroupNames []string `json:"GroupName"` // (必填)新增分组列表
|
||||||
|
JoinedUserIds []string `json:"To_Account"` // (必填)需要加入新增分组的好友的 UserID 列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加分组(响应)
|
||||||
|
addGroupsResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
ErrorDisplay string `json:"ErrorDisplay"` // 详细的客户端展示信息
|
||||||
|
FailUserIds []string `json:"Fail_Account"` // 返回处理失败的用户列表,仅当存在失败用户时才返回该字段
|
||||||
|
CurrentSequence int `json:"CurrentSequence"` // 返回最新的分组 Sequence
|
||||||
|
Results []*Result `json:"ResultItem"` // 好友加入新增分组的结果对象数组
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除分组(请求)
|
||||||
|
deleteGroupsReq struct {
|
||||||
|
UserId string `json:"From_Account"` // (必填)需要删除该 UserID 的分组
|
||||||
|
GroupNames []string `json:"GroupName"` // (必填)要删除的分组列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除分组(响应)
|
||||||
|
deleteGroupsResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
CurrentSequence int `json:"CurrentSequence"` // 返回最新的分组 Sequence
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拉取分组(请求)
|
||||||
|
getGroupsReq struct {
|
||||||
|
UserId string `json:"From_Account"` // (必填)指定要拉取分组的用户的 UserID
|
||||||
|
LastSequence int `json:"LastSequence"` // (必填)上一次拉取分组时后台返回给客户端的 Seq,初次拉取时为0,只有 GroupName 为空时有效
|
||||||
|
NeedFriend NeedFriendType `json:"NeedFriend"` // (选填)是否需要拉取分组下的 User 列表
|
||||||
|
GroupNames []string `json:"GroupName"` // (选填)要拉取的分组名称
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拉取分组(响应)
|
||||||
|
getGroupsResp struct {
|
||||||
|
types.ActionBaseResp
|
||||||
|
CurrentSequence int `json:"CurrentSequence"` // 返回最新的分组 Sequence
|
||||||
|
Results []*GroupResult `json:"ResultItem"` // 拉取分组的结果对象数组
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupResult 分组结果
|
||||||
|
GroupResult struct {
|
||||||
|
GroupName string `json:"GroupName"` // 分组名
|
||||||
|
FriendNumber int `json:"FriendNumber"` // 该分组下的好友数量
|
||||||
|
UserIds []string `json:"To_Account"` // 该分组下的好友的 UserID
|
||||||
|
}
|
||||||
|
)
|
Loading…
Reference in New Issue
Block a user