refactor: 3.7.0 code conventions. (#2148)
* Script Refactoring * Script Refactoring * Script Refactoring * Script Refactoring * Script Refactoring * Script Refactoring * Script Refactoring * Script Refactoring * Script Refactoring * Script Refactoring * Script Refactoring * Script Refactoring * feat: add code lint * feat: add code lint * Script Refactoring * Script Refactoring * Script Refactoring * Script Refactoring * Script Refactoring * Script Refactoring * Script Refactoring * Script Refactoring * Script Refactoring * Script Refactoring * Script Refactoring * Script Refactoring * Script Refactoring * Script Refactoring * Script Refactoring * Script Refactoring * Script Refactoring * feat: code format * Script Refactoring * Script Refactoring * Script Refactoring * Adjust MinIO configuration settings * Adjust configuration settings * Adjust configuration settings * refactor: config change. * refactor: webhooks update. * Adjust configuration settings * refactor: webhooks update. * Adjust configuration settings * Adjust configuration settings * Adjust configuration settings * feat: s3 api addr * refactor: webhooks update. * Adjust configuration settings * Adjust configuration settings * Adjust configuration settings * Adjust configuration settings * Adjust configuration settings * Adjust configuration settings * Adjust configuration settings * refactor: webhooks update. * refactor: kafka update. * Simplify the Docker Compose configuration, remove unnecessary environment variables, and eliminate the gateway service. * refactor: kafka update. * refactor: kafka update. * Simplify the Docker Compose configuration, remove unnecessary environment variables, and eliminate the gateway service. * Simplify the Docker Compose configuration, remove unnecessary environment variables, and eliminate the gateway service. * Windows can compile and run. * Windows can compile and run. * refactor: kafka update. * feat: msg cache split * refactor: webhooks update * refactor: webhooks update * refactor: friends update * refactor: group update * refactor: third update * refactor: api update * refactor: crontab update * refactor: msggateway update * mage * mage * refactor: all module update. * check * refactor: all module update. * load config * load config * load config * load config * refactor: all module update. * refactor: all module update. * refactor: all module update. * refactor: all module update. * refactor: all module update. * Optimize Docker configuration and script. * refactor: all module update. * Optimize Docker configuration and script. * Optimize Docker configuration and script. * Optimize Docker configuration and script. * refactor: all module update. * Optimize Docker configuration and script. * refactor: all module update. * refactor: all module update. * Optimize Docker configuration and script. * Optimize Docker configuration and script. * Optimize Docker configuration and script. * Optimize Docker configuration and script. * Optimize Docker configuration and script. * Optimize Docker configuration and script. * update tools * update tools * Optimize Docker configuration and script. * Optimize Docker configuration and script. * Optimize Docker configuration and script. * Optimize Docker configuration and script. * Optimize Docker configuration and script. * Optimize Docker configuration and script. * Optimize Docker configuration and script. * Optimize Docker configuration and script. * Optimize Docker configuration and script. * Optimize Docker configuration and script. * Optimize Docker configuration and script. * update protocol * Optimize Docker configuration and script. * Optimize Docker configuration and script. * refactor: all module update. * Optimize Docker configuration and script. * Optimize Docker configuration and script. * Optimize Docker configuration and script. * Optimize Docker configuration and script. * refactor: api remove token auth by redis directly. * Code Refactoring * refactor: websocket auth change to call rpc of auth. * refactor: kick online user and remove token change to call auth rpc. * refactor: kick online user and remove token change to call auth rpc. * refactor: remove msggateway redis. * refactor: cmd update. * refactor: cmd update. * refactor: cmd update. * refactor: cmd update. * refactor: cmd update. * refactor: cmd update. * refactor: cmd update. * refactor: cmd update. * refactor: cmd update. * refactor: cmd update. * refactor: cmd update. * refactor: cmd update. * refactor: cmd update. * refactor: cmd update. * refactor: cmd update. * refactor: cmd update. * refactor: cmd update. * refactor: cmd update. * refactor: cmd update. * refactor: cmd update. * refactor: cmd update. * refactor: cmd update. * refactor: cmd update. * refactor: cmd update. * refactor: cmd update. * refactor: cmd update. * refactor webhook * refactor: cmd update. * refactor: cmd update. * refactor: cmd update. * refactor: cmd update. * refactor webhook * refactor: cmd update. * refactor: cmd update. * fix: runtime: goroutine stack exceeds * refactor: cmd update. * refactor notification * refactor notification * refactor * refactor: cmd update. * refactor: cmd update. * refactor * refactor * refactor * protojson * protojson * protojson * go mod * wrapperspb * refactor: cmd update. * refactor: cmd update. * refactor: cmd update. * refactor: context update. * refactor: websocket update info. * refactor: websocket update info. * refactor: websocket update info. * refactor: websocket update info. * refactor: api name change. * refactor: debug info. * refactor: debug info. * refactor: debug info. * fix: update file * refactor * refactor * refactor: debug info. * refactor: debug info. * refactor: debug info. * refactor: debug info. * refactor: debug info. * refactor: debug info. * fix: callback update. * fix: callback update. * refactor * fix: update message. * fix: msg cache timeout. * refactor * refactor * fix: push update. * fix: push update. * fix: push update. * fix: push update. * fix: push update. * fix: push update. * fix: push update. * fix: websocket handle error remove when upgrade error. --------- Co-authored-by: skiffer-git <44203734@qq.com> Co-authored-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com> Co-authored-by: withchao <993506633@qq.com>
This commit is contained in:
@@ -15,10 +15,10 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/OpenIMSDK/protocol/auth"
|
||||
"github.com/OpenIMSDK/tools/a2r"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
"github.com/openimsdk/protocol/auth"
|
||||
"github.com/openimsdk/tools/a2r"
|
||||
)
|
||||
|
||||
type AuthApi rpcclient.Auth
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/OpenIMSDK/protocol/conversation"
|
||||
"github.com/OpenIMSDK/tools/a2r"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
"github.com/openimsdk/protocol/conversation"
|
||||
"github.com/openimsdk/tools/a2r"
|
||||
)
|
||||
|
||||
type ConversationApi rpcclient.Conversation
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
)
|
||||
|
||||
// RequiredIf validates if the specified field is required based on the session type.
|
||||
@@ -26,7 +26,7 @@ func RequiredIf(fl validator.FieldLevel) bool {
|
||||
switch sessionType {
|
||||
case constant.SingleChatType, constant.NotificationChatType:
|
||||
return fl.FieldName() != "RecvID" || fl.Field().String() != ""
|
||||
case constant.GroupChatType, constant.SuperGroupChatType:
|
||||
case constant.WriteGroupChatType, constant.ReadGroupChatType:
|
||||
return fl.FieldName() != "GroupID" || fl.Field().String() != ""
|
||||
default:
|
||||
return true
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/OpenIMSDK/protocol/friend"
|
||||
"github.com/OpenIMSDK/tools/a2r"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
"github.com/openimsdk/protocol/friend"
|
||||
"github.com/openimsdk/tools/a2r"
|
||||
)
|
||||
|
||||
type FriendApi rpcclient.Friend
|
||||
|
||||
+4
-12
@@ -15,10 +15,10 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/OpenIMSDK/protocol/group"
|
||||
"github.com/OpenIMSDK/tools/a2r"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
"github.com/openimsdk/protocol/group"
|
||||
"github.com/openimsdk/tools/a2r"
|
||||
)
|
||||
|
||||
type GroupApi rpcclient.Group
|
||||
@@ -115,22 +115,14 @@ func (o *GroupApi) GetGroupAbstractInfo(c *gin.Context) {
|
||||
a2r.Call(group.GroupClient.GetGroupAbstractInfo, o.Client, c)
|
||||
}
|
||||
|
||||
//func (g *Group) SetGroupMemberNickname(c *gin.Context) {
|
||||
// func (g *Group) SetGroupMemberNickname(c *gin.Context) {
|
||||
// a2r.Call(group.GroupClient.SetGroupMemberNickname, g.userClient, c)
|
||||
//}
|
||||
//
|
||||
//func (g *Group) GetGroupAllMemberList(c *gin.Context) {
|
||||
// func (g *Group) GetGroupAllMemberList(c *gin.Context) {
|
||||
// a2r.Call(group.GroupClient.GetGroupAllMember, g.userClient, c)
|
||||
//}
|
||||
|
||||
func (o *GroupApi) GetJoinedSuperGroupList(c *gin.Context) {
|
||||
a2r.Call(group.GroupClient.GetJoinedSuperGroupList, o.Client, c)
|
||||
}
|
||||
|
||||
func (o *GroupApi) GetSuperGroupsInfo(c *gin.Context) {
|
||||
a2r.Call(group.GroupClient.GetSuperGroupsInfo, o.Client, c)
|
||||
}
|
||||
|
||||
func (o *GroupApi) GroupCreateCount(c *gin.Context) {
|
||||
a2r.Call(group.GroupClient.GroupCreateCount, o.Client, c)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
"github.com/openimsdk/tools/utils/network"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister"
|
||||
ginprom "github.com/openimsdk/open-im-server/v3/pkg/common/ginprometheus"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
|
||||
"github.com/openimsdk/tools/discovery"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/system/program"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
RpcConfig config.API
|
||||
MongodbConfig config.Mongo
|
||||
ZookeeperConfig config.ZooKeeper
|
||||
NotificationConfig config.Notification
|
||||
Share config.Share
|
||||
MinioConfig config.Minio
|
||||
}
|
||||
|
||||
func Start(ctx context.Context, index int, config *Config) error {
|
||||
apiPort, err := datautil.GetElemByIndex(config.RpcConfig.Api.Ports, index)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
prometheusPort, err := datautil.GetElemByIndex(config.RpcConfig.Prometheus.Ports, index)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var client discovery.SvcDiscoveryRegistry
|
||||
|
||||
// Determine whether zk is passed according to whether it is a clustered deployment
|
||||
client, err = kdisc.NewDiscoveryRegister(&config.ZookeeperConfig, &config.Share)
|
||||
if err != nil {
|
||||
return errs.WrapMsg(err, "failed to register discovery service")
|
||||
}
|
||||
|
||||
if err = client.CreateRpcRootNodes(config.Share.RpcRegisterName.GetServiceNames()); err != nil {
|
||||
return errs.WrapMsg(err, "failed to create RPC root nodes")
|
||||
}
|
||||
|
||||
var (
|
||||
netDone = make(chan struct{}, 1)
|
||||
netErr error
|
||||
)
|
||||
|
||||
router := newGinRouter(client, config)
|
||||
if config.RpcConfig.Prometheus.Enable {
|
||||
go func() {
|
||||
p := ginprom.NewPrometheus("app", prommetrics.GetGinCusMetrics("Api"))
|
||||
p.SetListenAddress(fmt.Sprintf(":%d", prometheusPort))
|
||||
if err = p.Use(router); err != nil && err != http.ErrServerClosed {
|
||||
netErr = errs.WrapMsg(err, fmt.Sprintf("prometheus start err: %d", prometheusPort))
|
||||
netDone <- struct{}{}
|
||||
}
|
||||
}()
|
||||
|
||||
}
|
||||
address := net.JoinHostPort(network.GetListenIP(config.RpcConfig.Api.ListenIP), strconv.Itoa(apiPort))
|
||||
|
||||
server := http.Server{Addr: address, Handler: router}
|
||||
log.CInfo(ctx, "API server is initializing", "address", address, "apiPort", apiPort, "prometheusPort", prometheusPort)
|
||||
go func() {
|
||||
err = server.ListenAndServe()
|
||||
if err != nil && err != http.ErrServerClosed {
|
||||
netErr = errs.WrapMsg(err, fmt.Sprintf("api start err: %s", server.Addr))
|
||||
netDone <- struct{}{}
|
||||
|
||||
}
|
||||
}()
|
||||
|
||||
sigs := make(chan os.Signal, 1)
|
||||
signal.Notify(sigs, syscall.SIGTERM)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
select {
|
||||
case <-sigs:
|
||||
program.SIGTERMExit()
|
||||
err := server.Shutdown(ctx)
|
||||
if err != nil {
|
||||
return errs.WrapMsg(err, "shutdown err")
|
||||
}
|
||||
case <-netDone:
|
||||
close(netDone)
|
||||
return netErr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+42
-37
@@ -15,15 +15,6 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/OpenIMSDK/protocol/msg"
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
"github.com/OpenIMSDK/tools/a2r"
|
||||
"github.com/OpenIMSDK/tools/apiresp"
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/OpenIMSDK/tools/mcontext"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
@@ -31,23 +22,38 @@ import (
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
"github.com/openimsdk/protocol/msg"
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
"github.com/openimsdk/tools/a2r"
|
||||
"github.com/openimsdk/tools/apiresp"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/mcontext"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
"github.com/openimsdk/tools/utils/idutil"
|
||||
"github.com/openimsdk/tools/utils/jsonutil"
|
||||
"github.com/openimsdk/tools/utils/timeutil"
|
||||
)
|
||||
|
||||
type MessageApi struct {
|
||||
*rpcclient.Message
|
||||
validate *validator.Validate
|
||||
userRpcClient *rpcclient.UserRpcClient
|
||||
imAdminUserID []string
|
||||
}
|
||||
|
||||
func NewMessageApi(msgRpcClient *rpcclient.Message, userRpcClient *rpcclient.User) MessageApi {
|
||||
return MessageApi{Message: msgRpcClient, validate: validator.New(), userRpcClient: rpcclient.NewUserRpcClientByUser(userRpcClient)}
|
||||
func NewMessageApi(msgRpcClient *rpcclient.Message, userRpcClient *rpcclient.User,
|
||||
imAdminUserID []string) MessageApi {
|
||||
return MessageApi{Message: msgRpcClient, validate: validator.New(),
|
||||
userRpcClient: rpcclient.NewUserRpcClientByUser(userRpcClient), imAdminUserID: imAdminUserID}
|
||||
}
|
||||
|
||||
func (MessageApi) SetOptions(options map[string]bool, value bool) {
|
||||
utils.SetSwitchFromOptions(options, constant.IsHistory, value)
|
||||
utils.SetSwitchFromOptions(options, constant.IsPersistent, value)
|
||||
utils.SetSwitchFromOptions(options, constant.IsSenderSync, value)
|
||||
utils.SetSwitchFromOptions(options, constant.IsConversationUpdate, value)
|
||||
datautil.SetSwitchFromOptions(options, constant.IsHistory, value)
|
||||
datautil.SetSwitchFromOptions(options, constant.IsPersistent, value)
|
||||
datautil.SetSwitchFromOptions(options, constant.IsSenderSync, value)
|
||||
datautil.SetSwitchFromOptions(options, constant.IsConversationUpdate, value)
|
||||
}
|
||||
|
||||
func (m MessageApi) newUserSendMsgReq(_ *gin.Context, params *apistruct.SendMsg) *msg.SendMsgReq {
|
||||
@@ -56,8 +62,8 @@ func (m MessageApi) newUserSendMsgReq(_ *gin.Context, params *apistruct.SendMsg)
|
||||
switch params.ContentType {
|
||||
case constant.OANotification:
|
||||
notification := sdkws.NotificationElem{}
|
||||
notification.Detail = utils.StructToJsonString(params.Content)
|
||||
newContent = utils.StructToJsonString(¬ification)
|
||||
notification.Detail = jsonutil.StructToJsonString(params.Content)
|
||||
newContent = jsonutil.StructToJsonString(¬ification)
|
||||
case constant.Text:
|
||||
fallthrough
|
||||
case constant.Picture:
|
||||
@@ -71,19 +77,19 @@ func (m MessageApi) newUserSendMsgReq(_ *gin.Context, params *apistruct.SendMsg)
|
||||
case constant.File:
|
||||
fallthrough
|
||||
default:
|
||||
newContent = utils.StructToJsonString(params.Content)
|
||||
newContent = jsonutil.StructToJsonString(params.Content)
|
||||
}
|
||||
if params.IsOnlineOnly {
|
||||
m.SetOptions(options, false)
|
||||
}
|
||||
if params.NotOfflinePush {
|
||||
utils.SetSwitchFromOptions(options, constant.IsOfflinePush, false)
|
||||
datautil.SetSwitchFromOptions(options, constant.IsOfflinePush, false)
|
||||
}
|
||||
pbData := msg.SendMsgReq{
|
||||
MsgData: &sdkws.MsgData{
|
||||
SendID: params.SendID,
|
||||
GroupID: params.GroupID,
|
||||
ClientMsgID: utils.GetMsgID(params.SendID),
|
||||
ClientMsgID: idutil.GetMsgIDByMD5(params.SendID),
|
||||
SenderPlatformID: params.SenderPlatformID,
|
||||
SenderNickname: params.SenderNickname,
|
||||
SenderFaceURL: params.SenderFaceURL,
|
||||
@@ -91,7 +97,7 @@ func (m MessageApi) newUserSendMsgReq(_ *gin.Context, params *apistruct.SendMsg)
|
||||
MsgFrom: constant.SysMsgType,
|
||||
ContentType: params.ContentType,
|
||||
Content: []byte(newContent),
|
||||
CreateTime: utils.GetCurrentTimestampByMill(),
|
||||
CreateTime: timeutil.GetCurrentTimestampByMill(),
|
||||
SendTime: params.SendTime,
|
||||
Options: options,
|
||||
OfflinePushInfo: params.OfflinePushInfo,
|
||||
@@ -173,14 +179,14 @@ func (m *MessageApi) getSendMsgReq(c *gin.Context, req apistruct.SendMsg) (sendM
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, errs.ErrArgs.WithDetail("not support err contentType")
|
||||
return nil, errs.WrapMsg(errs.ErrArgs, "unsupported content type", "contentType", req.ContentType)
|
||||
}
|
||||
if err := mapstructure.WeakDecode(req.Content, &data); err != nil {
|
||||
return nil, err
|
||||
return nil, errs.WrapMsg(err, "failed to decode message content")
|
||||
}
|
||||
log.ZDebug(c, "getSendMsgReq", "req", req.Content)
|
||||
log.ZDebug(c, "getSendMsgReq", "decodedContent", data)
|
||||
if err := m.validate.Struct(data); err != nil {
|
||||
return nil, err
|
||||
return nil, errs.WrapMsg(err, "validation error")
|
||||
}
|
||||
return m.newUserSendMsgReq(c, &req), nil
|
||||
}
|
||||
@@ -198,9 +204,9 @@ func (m *MessageApi) SendMessage(c *gin.Context) {
|
||||
}
|
||||
|
||||
// Check if the user has the app manager role.
|
||||
if !authverify.IsAppManagerUid(c, m.Config) {
|
||||
if !authverify.IsAppManagerUid(c, m.imAdminUserID) {
|
||||
// Respond with a permission error if the user is not an app manager.
|
||||
apiresp.GinError(c, errs.ErrNoPermission.Wrap("only app manager can send message"))
|
||||
apiresp.GinError(c, errs.ErrNoPermission.WrapMsg("only app manager can send message"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -253,16 +259,16 @@ func (m *MessageApi) SendBusinessNotification(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if !authverify.IsAppManagerUid(c, m.Config) {
|
||||
apiresp.GinError(c, errs.ErrNoPermission.Wrap("only app manager can send message"))
|
||||
if !authverify.IsAppManagerUid(c, m.imAdminUserID) {
|
||||
apiresp.GinError(c, errs.ErrNoPermission.WrapMsg("only app manager can send message"))
|
||||
return
|
||||
}
|
||||
sendMsgReq := msg.SendMsgReq{
|
||||
MsgData: &sdkws.MsgData{
|
||||
SendID: req.SendUserID,
|
||||
RecvID: req.RecvUserID,
|
||||
Content: []byte(utils.StructToJsonString(&sdkws.NotificationElem{
|
||||
Detail: utils.StructToJsonString(&struct {
|
||||
Content: []byte(jsonutil.StructToJsonString(&sdkws.NotificationElem{
|
||||
Detail: jsonutil.StructToJsonString(&struct {
|
||||
Key string `json:"key"`
|
||||
Data string `json:"data"`
|
||||
}{Key: req.Key, Data: req.Data}),
|
||||
@@ -270,9 +276,9 @@ func (m *MessageApi) SendBusinessNotification(c *gin.Context) {
|
||||
MsgFrom: constant.SysMsgType,
|
||||
ContentType: constant.BusinessNotification,
|
||||
SessionType: constant.SingleChatType,
|
||||
CreateTime: utils.GetCurrentTimestampByMill(),
|
||||
ClientMsgID: utils.GetMsgID(mcontext.GetOpUserID(c)),
|
||||
Options: config.GetOptionsByNotification(config.NotificationConf{
|
||||
CreateTime: timeutil.GetCurrentTimestampByMill(),
|
||||
ClientMsgID: idutil.GetMsgIDByMD5(mcontext.GetOpUserID(c)),
|
||||
Options: config.GetOptionsByNotification(config.NotificationConfig{
|
||||
IsSendMsg: false,
|
||||
ReliabilityLevel: 1,
|
||||
UnreadCount: false,
|
||||
@@ -296,9 +302,8 @@ func (m *MessageApi) BatchSendMsg(c *gin.Context) {
|
||||
apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap())
|
||||
return
|
||||
}
|
||||
log.ZInfo(c, "BatchSendMsg", "req", req)
|
||||
if err := authverify.CheckAdmin(c, m.Config); err != nil {
|
||||
apiresp.GinError(c, errs.ErrNoPermission.Wrap("only app manager can send message"))
|
||||
if err := authverify.CheckAdmin(c, m.imAdminUserID); err != nil {
|
||||
apiresp.GinError(c, errs.ErrNoPermission.WrapMsg("only app manager can send message"))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -1,135 +1,25 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/OpenIMSDK/tools/apiresp"
|
||||
"github.com/OpenIMSDK/tools/discoveryregistry"
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/OpenIMSDK/tools/mw"
|
||||
"github.com/OpenIMSDK/tools/tokenverify"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gin-gonic/gin/binding"
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
|
||||
kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister"
|
||||
ginprom "github.com/openimsdk/open-im-server/v3/pkg/common/ginprometheus"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
"github.com/openimsdk/tools/apiresp"
|
||||
"github.com/openimsdk/tools/discovery"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/mw"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func Start(config *config.GlobalConfig, port int, proPort int) error {
|
||||
if port == 0 || proPort == 0 {
|
||||
err := "port or proPort is empty:" + strconv.Itoa(port) + "," + strconv.Itoa(proPort)
|
||||
return errs.Wrap(fmt.Errorf(err))
|
||||
}
|
||||
rdb, err := cache.NewRedis(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var client discoveryregistry.SvcDiscoveryRegistry
|
||||
|
||||
// Determine whether zk is passed according to whether it is a clustered deployment
|
||||
client, err = kdisc.NewDiscoveryRegister(config)
|
||||
if err != nil {
|
||||
return errs.Wrap(err, "register discovery err")
|
||||
}
|
||||
|
||||
if err = client.CreateRpcRootNodes(config.GetServiceNames()); err != nil {
|
||||
return errs.Wrap(err, "create rpc root nodes error")
|
||||
}
|
||||
|
||||
if err = client.RegisterConf2Registry(constant.OpenIMCommonConfigKey, config.EncodeConfig()); err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
var (
|
||||
netDone = make(chan struct{}, 1)
|
||||
netErr error
|
||||
)
|
||||
router := newGinRouter(client, rdb, config)
|
||||
if config.Prometheus.Enable {
|
||||
go func() {
|
||||
p := ginprom.NewPrometheus("app", prommetrics.GetGinCusMetrics("Api"))
|
||||
p.SetListenAddress(fmt.Sprintf(":%d", proPort))
|
||||
if err = p.Use(router); err != nil && err != http.ErrServerClosed {
|
||||
netErr = errs.Wrap(err, fmt.Sprintf("prometheus start err: %d", proPort))
|
||||
netDone <- struct{}{}
|
||||
}
|
||||
}()
|
||||
|
||||
}
|
||||
|
||||
var address string
|
||||
if config.Api.ListenIP != "" {
|
||||
address = net.JoinHostPort(config.Api.ListenIP, strconv.Itoa(port))
|
||||
} else {
|
||||
address = net.JoinHostPort("0.0.0.0", strconv.Itoa(port))
|
||||
}
|
||||
|
||||
server := http.Server{Addr: address, Handler: router}
|
||||
|
||||
go func() {
|
||||
err = server.ListenAndServe()
|
||||
if err != nil && err != http.ErrServerClosed {
|
||||
netErr = errs.Wrap(err, fmt.Sprintf("api start err: %s", server.Addr))
|
||||
netDone <- struct{}{}
|
||||
|
||||
}
|
||||
}()
|
||||
|
||||
sigs := make(chan os.Signal, 1)
|
||||
signal.Notify(sigs, syscall.SIGTERM)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
select {
|
||||
case <-sigs:
|
||||
util.SIGTERMExit()
|
||||
err := server.Shutdown(ctx)
|
||||
if err != nil {
|
||||
return errs.Wrap(err, "shutdown err")
|
||||
}
|
||||
case <-netDone:
|
||||
close(netDone)
|
||||
return netErr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newGinRouter(disCov discoveryregistry.SvcDiscoveryRegistry, rdb redis.UniversalClient, config *config.GlobalConfig) *gin.Engine {
|
||||
disCov.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin")))
|
||||
func newGinRouter(disCov discovery.SvcDiscoveryRegistry, config *Config) *gin.Engine {
|
||||
disCov.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||
grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin")))
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
r := gin.New()
|
||||
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
|
||||
@@ -137,17 +27,18 @@ func newGinRouter(disCov discoveryregistry.SvcDiscoveryRegistry, rdb redis.Unive
|
||||
}
|
||||
r.Use(gin.Recovery(), mw.CorsHandler(), mw.GinParseOperationID())
|
||||
// init rpc client here
|
||||
userRpc := rpcclient.NewUser(disCov, config)
|
||||
groupRpc := rpcclient.NewGroup(disCov, config)
|
||||
friendRpc := rpcclient.NewFriend(disCov, config)
|
||||
messageRpc := rpcclient.NewMessage(disCov, config)
|
||||
conversationRpc := rpcclient.NewConversation(disCov, config)
|
||||
authRpc := rpcclient.NewAuth(disCov, config)
|
||||
thirdRpc := rpcclient.NewThird(disCov, config)
|
||||
userRpc := rpcclient.NewUser(disCov, config.Share.RpcRegisterName.User, config.Share.RpcRegisterName.MessageGateway,
|
||||
config.Share.IMAdminUserID)
|
||||
groupRpc := rpcclient.NewGroup(disCov, config.Share.RpcRegisterName.Group)
|
||||
friendRpc := rpcclient.NewFriend(disCov, config.Share.RpcRegisterName.Friend)
|
||||
messageRpc := rpcclient.NewMessage(disCov, config.Share.RpcRegisterName.Msg)
|
||||
conversationRpc := rpcclient.NewConversation(disCov, config.Share.RpcRegisterName.Conversation)
|
||||
authRpc := rpcclient.NewAuth(disCov, config.Share.RpcRegisterName.Auth)
|
||||
thirdRpc := rpcclient.NewThird(disCov, config.Share.RpcRegisterName.Third, config.RpcConfig.Prometheus.GrafanaURL)
|
||||
|
||||
u := NewUserApi(*userRpc)
|
||||
m := NewMessageApi(messageRpc, userRpc)
|
||||
ParseToken := GinParseToken(rdb, config)
|
||||
m := NewMessageApi(messageRpc, userRpc, config.Share.IMAdminUserID)
|
||||
ParseToken := GinParseToken(authRpc)
|
||||
userRouterGroup := r.Group("/user")
|
||||
{
|
||||
userRouterGroup.POST("/user_register", u.UserRegister)
|
||||
@@ -224,11 +115,6 @@ func newGinRouter(disCov discoveryregistry.SvcDiscoveryRegistry, rdb redis.Unive
|
||||
groupRouterGroup.POST("/get_groups", g.GetGroups)
|
||||
groupRouterGroup.POST("/get_group_member_user_id", g.GetGroupMemberUserIDs)
|
||||
}
|
||||
superGroupRouterGroup := r.Group("/super_group", ParseToken)
|
||||
{
|
||||
superGroupRouterGroup.POST("/get_joined_group_list", g.GetJoinedSuperGroupList)
|
||||
superGroupRouterGroup.POST("/get_groups_info", g.GetSuperGroupsInfo)
|
||||
}
|
||||
// certificate
|
||||
authRouterGroup := r.Group("/auth")
|
||||
{
|
||||
@@ -309,68 +195,26 @@ func newGinRouter(disCov discoveryregistry.SvcDiscoveryRegistry, rdb redis.Unive
|
||||
return r
|
||||
}
|
||||
|
||||
func GinParseToken(rdb redis.UniversalClient, config *config.GlobalConfig) gin.HandlerFunc {
|
||||
dataBase := controller.NewAuthDatabase(
|
||||
cache.NewMsgCacheModel(rdb, config),
|
||||
config.Secret,
|
||||
config.TokenPolicy.Expire,
|
||||
config,
|
||||
)
|
||||
func GinParseToken(authRPC *rpcclient.Auth) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
switch c.Request.Method {
|
||||
case http.MethodPost:
|
||||
token := c.Request.Header.Get(constant.Token)
|
||||
if token == "" {
|
||||
log.ZWarn(c, "header get token error", errs.ErrArgs.Wrap("header must have token"))
|
||||
apiresp.GinError(c, errs.ErrArgs.Wrap("header must have token"))
|
||||
log.ZWarn(c, "header get token error", servererrs.ErrArgs.WrapMsg("header must have token"))
|
||||
apiresp.GinError(c, servererrs.ErrArgs.WrapMsg("header must have token"))
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
claims, err := tokenverify.GetClaimFromToken(token, authverify.Secret(config.Secret))
|
||||
resp, err := authRPC.ParseToken(c, token)
|
||||
if err != nil {
|
||||
log.ZWarn(c, "jwt get token error", errs.ErrTokenUnknown.Wrap())
|
||||
apiresp.GinError(c, errs.ErrTokenUnknown.Wrap())
|
||||
apiresp.GinError(c, err)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
m, err := dataBase.GetTokensWithoutError(c, claims.UserID, claims.PlatformID)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, errs.ErrTokenNotExist.Wrap())
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
if len(m) == 0 {
|
||||
apiresp.GinError(c, errs.ErrTokenNotExist.Wrap())
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
if v, ok := m[token]; ok {
|
||||
switch v {
|
||||
case constant.NormalToken:
|
||||
case constant.KickedToken:
|
||||
apiresp.GinError(c, errs.ErrTokenKicked.Wrap())
|
||||
c.Abort()
|
||||
return
|
||||
default:
|
||||
apiresp.GinError(c, errs.ErrTokenUnknown.Wrap())
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
} else {
|
||||
apiresp.GinError(c, errs.ErrTokenNotExist.Wrap())
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
c.Set(constant.OpUserPlatform, constant.PlatformIDToName(claims.PlatformID))
|
||||
c.Set(constant.OpUserID, claims.UserID)
|
||||
c.Set(constant.OpUserPlatform, constant.PlatformIDToName(int(resp.PlatformID)))
|
||||
c.Set(constant.OpUserID, resp.UserID)
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// // handleGinError logs and returns an error response through Gin context.
|
||||
// func handleGinError(c *gin.Context, logMessage string, errType errs.CodeError, detail string) {
|
||||
// wrappedErr := errType.Wrap(detail)
|
||||
// apiresp.GinError(c, wrappedErr)
|
||||
// c.Abort()
|
||||
// }
|
||||
@@ -15,10 +15,10 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/OpenIMSDK/protocol/user"
|
||||
"github.com/OpenIMSDK/tools/a2r"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
"github.com/openimsdk/protocol/user"
|
||||
"github.com/openimsdk/tools/a2r"
|
||||
)
|
||||
|
||||
type StatisticsApi rpcclient.User
|
||||
|
||||
+50
-8
@@ -15,16 +15,20 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"google.golang.org/grpc"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/third"
|
||||
"github.com/OpenIMSDK/tools/a2r"
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/OpenIMSDK/tools/mcontext"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
"github.com/openimsdk/protocol/third"
|
||||
"github.com/openimsdk/tools/a2r"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/mcontext"
|
||||
)
|
||||
|
||||
type ThirdApi rpcclient.Third
|
||||
@@ -43,6 +47,35 @@ func (o *ThirdApi) SetAppBadge(c *gin.Context) {
|
||||
|
||||
// #################### s3 ####################
|
||||
|
||||
func setURLPrefixOption[A, B, C any](_ func(client C, ctx context.Context, req *A, options ...grpc.CallOption) (*B, error), fn func(*A) error) *a2r.Option[A, B] {
|
||||
return &a2r.Option[A, B]{
|
||||
BindAfter: fn,
|
||||
}
|
||||
}
|
||||
|
||||
func setURLPrefix(c *gin.Context, urlPrefix *string) error {
|
||||
host := c.GetHeader("X-Request-Api")
|
||||
if host != "" {
|
||||
if strings.HasSuffix(host, "/") {
|
||||
*urlPrefix = host + "object/"
|
||||
return nil
|
||||
} else {
|
||||
*urlPrefix = host + "/object/"
|
||||
return nil
|
||||
}
|
||||
}
|
||||
u := url.URL{
|
||||
Scheme: "http",
|
||||
Host: c.Request.Host,
|
||||
Path: "/object/",
|
||||
}
|
||||
if c.Request.TLS != nil {
|
||||
u.Scheme = "https"
|
||||
}
|
||||
*urlPrefix = u.String()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *ThirdApi) PartLimit(c *gin.Context) {
|
||||
a2r.Call(third.ThirdClient.PartLimit, o.Client, c)
|
||||
}
|
||||
@@ -52,7 +85,10 @@ func (o *ThirdApi) PartSize(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (o *ThirdApi) InitiateMultipartUpload(c *gin.Context) {
|
||||
a2r.Call(third.ThirdClient.InitiateMultipartUpload, o.Client, c)
|
||||
opt := setURLPrefixOption(third.ThirdClient.InitiateMultipartUpload, func(req *third.InitiateMultipartUploadReq) error {
|
||||
return setURLPrefix(c, &req.UrlPrefix)
|
||||
})
|
||||
a2r.Call(third.ThirdClient.InitiateMultipartUpload, o.Client, c, opt)
|
||||
}
|
||||
|
||||
func (o *ThirdApi) AuthSign(c *gin.Context) {
|
||||
@@ -60,7 +96,10 @@ func (o *ThirdApi) AuthSign(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (o *ThirdApi) CompleteMultipartUpload(c *gin.Context) {
|
||||
a2r.Call(third.ThirdClient.CompleteMultipartUpload, o.Client, c)
|
||||
opt := setURLPrefixOption(third.ThirdClient.CompleteMultipartUpload, func(req *third.CompleteMultipartUploadReq) error {
|
||||
return setURLPrefix(c, &req.UrlPrefix)
|
||||
})
|
||||
a2r.Call(third.ThirdClient.CompleteMultipartUpload, o.Client, c, opt)
|
||||
}
|
||||
|
||||
func (o *ThirdApi) AccessURL(c *gin.Context) {
|
||||
@@ -72,7 +111,10 @@ func (o *ThirdApi) InitiateFormData(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (o *ThirdApi) CompleteFormData(c *gin.Context) {
|
||||
a2r.Call(third.ThirdClient.CompleteFormData, o.Client, c)
|
||||
opt := setURLPrefixOption(third.ThirdClient.CompleteFormData, func(req *third.CompleteFormDataReq) error {
|
||||
return setURLPrefix(c, &req.UrlPrefix)
|
||||
})
|
||||
a2r.Call(third.ThirdClient.CompleteFormData, o.Client, c, opt)
|
||||
}
|
||||
|
||||
func (o *ThirdApi) ObjectRedirect(c *gin.Context) {
|
||||
@@ -126,5 +168,5 @@ func (o *ThirdApi) SearchLogs(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (o *ThirdApi) GetPrometheus(c *gin.Context) {
|
||||
c.Redirect(http.StatusFound, o.Config.Prometheus.GrafanaUrl)
|
||||
c.Redirect(http.StatusFound, o.GrafanaUrl)
|
||||
}
|
||||
|
||||
@@ -15,15 +15,15 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/OpenIMSDK/protocol/msggateway"
|
||||
"github.com/OpenIMSDK/protocol/user"
|
||||
"github.com/OpenIMSDK/tools/a2r"
|
||||
"github.com/OpenIMSDK/tools/apiresp"
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
"github.com/openimsdk/protocol/msggateway"
|
||||
"github.com/openimsdk/protocol/user"
|
||||
"github.com/openimsdk/tools/a2r"
|
||||
"github.com/openimsdk/tools/apiresp"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/log"
|
||||
)
|
||||
|
||||
type UserApi rpcclient.User
|
||||
@@ -69,7 +69,7 @@ func (u *UserApi) GetUsersOnlineStatus(c *gin.Context) {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
conns, err := u.Discov.GetConns(c, u.Config.RpcRegisterName.OpenImMessageGatewayName)
|
||||
conns, err := u.Discov.GetConns(c, u.MessageGateWayRpcName)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
@@ -133,7 +133,7 @@ func (u *UserApi) GetUsersOnlineTokenDetail(c *gin.Context) {
|
||||
apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap())
|
||||
return
|
||||
}
|
||||
conns, err := u.Discov.GetConns(c, u.Config.RpcRegisterName.OpenImMessageGatewayName)
|
||||
conns, err := u.Discov.GetConns(c, u.MessageGateWayRpcName)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
|
||||
+11
-117
@@ -18,21 +18,17 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/OpenIMSDK/tools/mcontext"
|
||||
cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/http"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
"github.com/openimsdk/tools/mcontext"
|
||||
)
|
||||
|
||||
func CallbackUserOnline(ctx context.Context, globalConfig *config.GlobalConfig, userID string, platformID int, isAppBackground bool, connID string) error {
|
||||
if !globalConfig.Callback.CallbackUserOnline.Enable {
|
||||
return nil
|
||||
}
|
||||
func (ws *WsServer) webhookAfterUserOnline(ctx context.Context, after *config.AfterConfig, userID string, platformID int, isAppBackground bool, connID string) {
|
||||
req := cbapi.CallbackUserOnlineReq{
|
||||
UserStatusCallbackReq: cbapi.UserStatusCallbackReq{
|
||||
UserStatusBaseCallback: cbapi.UserStatusBaseCallback{
|
||||
CallbackCommand: cbapi.CallbackUserOnlineCommand,
|
||||
CallbackCommand: cbapi.CallbackAfterUserOnlineCommand,
|
||||
OperationID: mcontext.GetOperationID(ctx),
|
||||
PlatformID: platformID,
|
||||
Platform: constant.PlatformIDToName(platformID),
|
||||
@@ -43,21 +39,14 @@ func CallbackUserOnline(ctx context.Context, globalConfig *config.GlobalConfig,
|
||||
IsAppBackground: isAppBackground,
|
||||
ConnID: connID,
|
||||
}
|
||||
resp := cbapi.CommonCallbackResp{}
|
||||
if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, &req, &resp, globalConfig.Callback.CallbackUserOnline); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
ws.webhookClient.AsyncPost(ctx, req.GetCallbackCommand(), req, &cbapi.CommonCallbackResp{}, after)
|
||||
}
|
||||
|
||||
func CallbackUserOffline(ctx context.Context, globalConfig *config.GlobalConfig, userID string, platformID int, connID string) error {
|
||||
if !globalConfig.Callback.CallbackUserOffline.Enable {
|
||||
return nil
|
||||
}
|
||||
func (ws *WsServer) webhookAfterUserOffline(ctx context.Context, after *config.AfterConfig, userID string, platformID int, connID string) {
|
||||
req := &cbapi.CallbackUserOfflineReq{
|
||||
UserStatusCallbackReq: cbapi.UserStatusCallbackReq{
|
||||
UserStatusBaseCallback: cbapi.UserStatusBaseCallback{
|
||||
CallbackCommand: cbapi.CallbackUserOfflineCommand,
|
||||
CallbackCommand: cbapi.CallbackAfterUserOfflineCommand,
|
||||
OperationID: mcontext.GetOperationID(ctx),
|
||||
PlatformID: platformID,
|
||||
Platform: constant.PlatformIDToName(platformID),
|
||||
@@ -67,21 +56,14 @@ func CallbackUserOffline(ctx context.Context, globalConfig *config.GlobalConfig,
|
||||
Seq: time.Now().UnixMilli(),
|
||||
ConnID: connID,
|
||||
}
|
||||
resp := &cbapi.CallbackUserOfflineResp{}
|
||||
if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackUserOffline); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
ws.webhookClient.AsyncPost(ctx, req.GetCallbackCommand(), req, &cbapi.CallbackUserOfflineResp{}, after)
|
||||
}
|
||||
|
||||
func CallbackUserKickOff(ctx context.Context, globalConfig *config.GlobalConfig, userID string, platformID int) error {
|
||||
if !globalConfig.Callback.CallbackUserKickOff.Enable {
|
||||
return nil
|
||||
}
|
||||
func (ws *WsServer) webhookAfterUserKickOff(ctx context.Context, after *config.AfterConfig, userID string, platformID int) {
|
||||
req := &cbapi.CallbackUserKickOffReq{
|
||||
UserStatusCallbackReq: cbapi.UserStatusCallbackReq{
|
||||
UserStatusBaseCallback: cbapi.UserStatusBaseCallback{
|
||||
CallbackCommand: cbapi.CallbackUserKickOffCommand,
|
||||
CallbackCommand: cbapi.CallbackAfterUserKickOffCommand,
|
||||
OperationID: mcontext.GetOperationID(ctx),
|
||||
PlatformID: platformID,
|
||||
Platform: constant.PlatformIDToName(platformID),
|
||||
@@ -90,93 +72,5 @@ func CallbackUserKickOff(ctx context.Context, globalConfig *config.GlobalConfig,
|
||||
},
|
||||
Seq: time.Now().UnixMilli(),
|
||||
}
|
||||
resp := &cbapi.CommonCallbackResp{}
|
||||
if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackUserOffline); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
ws.webhookClient.AsyncPost(ctx, req.GetCallbackCommand(), req, &cbapi.CommonCallbackResp{}, after)
|
||||
}
|
||||
|
||||
// func callbackUserOnline(operationID, userID string, platformID int, token string, isAppBackground bool, connID
|
||||
// string) cbApi.CommonCallbackResp {
|
||||
// callbackResp := cbApi.CommonCallbackResp{OperationID: operationID}
|
||||
// if !config.Config.Callback.CallbackUserOnline.WithEnable {
|
||||
// return callbackResp
|
||||
// }
|
||||
// callbackUserOnlineReq := cbApi.CallbackUserOnlineReq{
|
||||
// Token: token,
|
||||
// UserStatusCallbackReq: cbApi.UserStatusCallbackReq{
|
||||
// UserStatusBaseCallback: cbApi.UserStatusBaseCallback{
|
||||
// CallbackCommand: constant.CallbackUserOnlineCommand,
|
||||
// OperationID: operationID,
|
||||
// PlatformID: int32(platformID),
|
||||
// Platform: constant.PlatformIDToName(platformID),
|
||||
// },
|
||||
// UserID: userID,
|
||||
// },
|
||||
// Seq: int(time.Now().UnixNano() / 1e6),
|
||||
// IsAppBackground: isAppBackground,
|
||||
// ConnID: connID,
|
||||
// }
|
||||
// callbackUserOnlineResp := &cbApi.CallbackUserOnlineResp{CommonCallbackResp: &callbackResp}
|
||||
// if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, constant.CallbackUserOnlineCommand,
|
||||
// callbackUserOnlineReq, callbackUserOnlineResp, config.Config.Callback.CallbackUserOnline.CallbackTimeOut); err != nil
|
||||
// {
|
||||
// callbackResp.ErrCode = http2.StatusInternalServerError
|
||||
// callbackResp.ErrMsg = err.Error()
|
||||
// }
|
||||
// return callbackResp
|
||||
//}
|
||||
//func callbackUserOffline(operationID, userID string, platformID int, connID string) cbApi.CommonCallbackResp {
|
||||
// callbackResp := cbApi.CommonCallbackResp{OperationID: operationID}
|
||||
// if !config.Config.Callback.CallbackUserOffline.WithEnable {
|
||||
// return callbackResp
|
||||
// }
|
||||
// callbackOfflineReq := cbApi.CallbackUserOfflineReq{
|
||||
// UserStatusCallbackReq: cbApi.UserStatusCallbackReq{
|
||||
// UserStatusBaseCallback: cbApi.UserStatusBaseCallback{
|
||||
// CallbackCommand: constant.CallbackUserOfflineCommand,
|
||||
// OperationID: operationID,
|
||||
// PlatformID: int32(platformID),
|
||||
// Platform: constant.PlatformIDToName(platformID),
|
||||
// },
|
||||
// UserID: userID,
|
||||
// },
|
||||
// Seq: int(time.Now().UnixNano() / 1e6),
|
||||
// ConnID: connID,
|
||||
// }
|
||||
// callbackUserOfflineResp := &cbApi.CallbackUserOfflineResp{CommonCallbackResp: &callbackResp}
|
||||
// if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, constant.CallbackUserOfflineCommand,
|
||||
// callbackOfflineReq, callbackUserOfflineResp, config.Config.Callback.CallbackUserOffline.CallbackTimeOut); err != nil
|
||||
// {
|
||||
// callbackResp.ErrCode = http2.StatusInternalServerError
|
||||
// callbackResp.ErrMsg = err.Error()
|
||||
// }
|
||||
// return callbackResp
|
||||
//}
|
||||
//func callbackUserKickOff(operationID string, userID string, platformID int) cbApi.CommonCallbackResp {
|
||||
// callbackResp := cbApi.CommonCallbackResp{OperationID: operationID}
|
||||
// if !config.Config.Callback.CallbackUserKickOff.WithEnable {
|
||||
// return callbackResp
|
||||
// }
|
||||
// callbackUserKickOffReq := cbApi.CallbackUserKickOffReq{
|
||||
// UserStatusCallbackReq: cbApi.UserStatusCallbackReq{
|
||||
// UserStatusBaseCallback: cbApi.UserStatusBaseCallback{
|
||||
// CallbackCommand: constant.CallbackUserKickOffCommand,
|
||||
// OperationID: operationID,
|
||||
// PlatformID: int32(platformID),
|
||||
// Platform: constant.PlatformIDToName(platformID),
|
||||
// },
|
||||
// UserID: userID,
|
||||
// },
|
||||
// Seq: int(time.Now().UnixNano() / 1e6),
|
||||
// }
|
||||
// callbackUserKickOffResp := &cbApi.CallbackUserKickOffResp{CommonCallbackResp: &callbackResp}
|
||||
// if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, constant.CallbackUserKickOffCommand,
|
||||
// callbackUserKickOffReq, callbackUserKickOffResp, config.Config.Callback.CallbackUserOffline.CallbackTimeOut); err !=
|
||||
// nil {
|
||||
// callbackResp.ErrCode = http2.StatusInternalServerError
|
||||
// callbackResp.ErrMsg = err.Error()
|
||||
// }
|
||||
// return callbackResp
|
||||
//}
|
||||
|
||||
@@ -16,28 +16,27 @@ package msggateway
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
"github.com/OpenIMSDK/tools/apiresp"
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/OpenIMSDK/tools/mcontext"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
"github.com/openimsdk/tools/apiresp"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/mcontext"
|
||||
"github.com/openimsdk/tools/utils/stringutil"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrConnClosed = errors.New("conn has closed")
|
||||
ErrNotSupportMessageProtocol = errors.New("not support message protocol")
|
||||
ErrClientClosed = errors.New("client actively close the connection")
|
||||
ErrPanic = errors.New("panic error")
|
||||
ErrConnClosed = errs.New("conn has closed")
|
||||
ErrNotSupportMessageProtocol = errs.New("not support message protocol")
|
||||
ErrClientClosed = errs.New("client actively close the connection")
|
||||
ErrPanic = errs.New("panic error")
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -75,32 +74,20 @@ type Client struct {
|
||||
token string
|
||||
}
|
||||
|
||||
// function not used
|
||||
// func newClient(ctx *UserConnContext, conn LongConn, isCompress bool) *Client {
|
||||
// return &Client{
|
||||
// w: new(sync.Mutex),
|
||||
// conn: conn,
|
||||
// PlatformID: utils.StringToInt(ctx.GetPlatformID()),
|
||||
// IsCompress: isCompress,
|
||||
// UserID: ctx.GetUserID(),
|
||||
// ctx: ctx,
|
||||
// }
|
||||
// }
|
||||
|
||||
// ResetClient updates the client's state with new connection and context information.
|
||||
func (c *Client) ResetClient(ctx *UserConnContext, conn LongConn, isBackground, isCompress bool, longConnServer LongConnServer, token string) {
|
||||
func (c *Client) ResetClient(ctx *UserConnContext, conn LongConn, longConnServer LongConnServer) {
|
||||
c.w = new(sync.Mutex)
|
||||
c.conn = conn
|
||||
c.PlatformID = utils.StringToInt(ctx.GetPlatformID())
|
||||
c.IsCompress = isCompress
|
||||
c.IsBackground = isBackground
|
||||
c.PlatformID = stringutil.StringToInt(ctx.GetPlatformID())
|
||||
c.IsCompress = ctx.GetCompression()
|
||||
c.IsBackground = ctx.GetBackground()
|
||||
c.UserID = ctx.GetUserID()
|
||||
c.ctx = ctx
|
||||
c.longConnServer = longConnServer
|
||||
c.IsBackground = false
|
||||
c.closed.Store(false)
|
||||
c.closedErr = nil
|
||||
c.token = token
|
||||
c.token = ctx.GetToken()
|
||||
}
|
||||
|
||||
func (c *Client) pingHandler(_ string) error {
|
||||
@@ -126,6 +113,7 @@ func (c *Client) readMessage() {
|
||||
c.conn.SetPingHandler(c.pingHandler)
|
||||
|
||||
for {
|
||||
log.ZDebug(c.ctx, "readMessage")
|
||||
messageType, message, returnErr := c.conn.ReadMessage()
|
||||
if returnErr != nil {
|
||||
log.ZWarn(c.ctx, "readMessage", returnErr, "messageType", messageType)
|
||||
@@ -187,7 +175,7 @@ func (c *Client) handleMessage(message []byte) error {
|
||||
}
|
||||
|
||||
if binaryReq.SendID != c.UserID {
|
||||
return errs.Wrap(errors.New("exception conn userID not same to req userID"), binaryReq.String())
|
||||
return errs.New("exception conn userID not same to req userID", "binaryReq", binaryReq.String())
|
||||
}
|
||||
|
||||
ctx := mcontext.WithMustInfoCtx(
|
||||
@@ -267,7 +255,7 @@ func (c *Client) replyMessage(ctx context.Context, binaryReq *Req, err error, re
|
||||
}
|
||||
|
||||
if binaryReq.ReqIdentifier == WsLogoutMsg {
|
||||
return errs.Wrap(errors.New("user logout"))
|
||||
return errs.New("user logout", "operationID", binaryReq.OperationID).Wrap()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -34,6 +34,7 @@ type Compressor interface {
|
||||
DeCompress(compressedData []byte) ([]byte, error)
|
||||
DecompressWithPool(compressedData []byte) ([]byte, error)
|
||||
}
|
||||
|
||||
type GzipCompressor struct {
|
||||
compressProtocol string
|
||||
}
|
||||
@@ -47,11 +48,11 @@ func (g *GzipCompressor) Compress(rawData []byte) ([]byte, error) {
|
||||
gz := gzip.NewWriter(&gzipBuffer)
|
||||
|
||||
if _, err := gz.Write(rawData); err != nil {
|
||||
return nil, errs.Wrap(err, "GzipCompressor.Compress: writing to gzip writer failed")
|
||||
return nil, errs.WrapMsg(err, "GzipCompressor.Compress: writing to gzip writer failed")
|
||||
}
|
||||
|
||||
if err := gz.Close(); err != nil {
|
||||
return nil, errs.Wrap(err, "GzipCompressor.Compress: closing gzip writer failed")
|
||||
return nil, errs.WrapMsg(err, "GzipCompressor.Compress: closing gzip writer failed")
|
||||
}
|
||||
|
||||
return gzipBuffer.Bytes(), nil
|
||||
@@ -65,10 +66,10 @@ func (g *GzipCompressor) CompressWithPool(rawData []byte) ([]byte, error) {
|
||||
gz.Reset(&gzipBuffer)
|
||||
|
||||
if _, err := gz.Write(rawData); err != nil {
|
||||
return nil, errs.Wrap(err, "GzipCompressor.CompressWithPool: error writing data")
|
||||
return nil, errs.WrapMsg(err, "GzipCompressor.CompressWithPool: error writing data")
|
||||
}
|
||||
if err := gz.Close(); err != nil {
|
||||
return nil, errs.Wrap(err, "GzipCompressor.CompressWithPool: error closing gzip writer")
|
||||
return nil, errs.WrapMsg(err, "GzipCompressor.CompressWithPool: error closing gzip writer")
|
||||
}
|
||||
return gzipBuffer.Bytes(), nil
|
||||
}
|
||||
@@ -77,16 +78,16 @@ func (g *GzipCompressor) DeCompress(compressedData []byte) ([]byte, error) {
|
||||
buff := bytes.NewBuffer(compressedData)
|
||||
reader, err := gzip.NewReader(buff)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err, "GzipCompressor.DeCompress: NewReader creation failed")
|
||||
return nil, errs.WrapMsg(err, "GzipCompressor.DeCompress: NewReader creation failed")
|
||||
}
|
||||
decompressedData, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err, "GzipCompressor.DeCompress: reading from gzip reader failed")
|
||||
return nil, errs.WrapMsg(err, "GzipCompressor.DeCompress: reading from gzip reader failed")
|
||||
}
|
||||
if err = reader.Close(); err != nil {
|
||||
// Even if closing the reader fails, we've successfully read the data,
|
||||
// so we return the decompressed data and an error indicating the close failure.
|
||||
return decompressedData, errs.Wrap(err, "GzipCompressor.DeCompress: closing gzip reader failed")
|
||||
return decompressedData, errs.WrapMsg(err, "GzipCompressor.DeCompress: closing gzip reader failed")
|
||||
}
|
||||
return decompressedData, nil
|
||||
}
|
||||
@@ -97,16 +98,16 @@ func (g *GzipCompressor) DecompressWithPool(compressedData []byte) ([]byte, erro
|
||||
|
||||
err := reader.Reset(bytes.NewReader(compressedData))
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err, "GzipCompressor.DecompressWithPool: resetting gzip reader failed")
|
||||
return nil, errs.WrapMsg(err, "GzipCompressor.DecompressWithPool: resetting gzip reader failed")
|
||||
}
|
||||
|
||||
decompressedData, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err, "GzipCompressor.DecompressWithPool: reading from pooled gzip reader failed")
|
||||
return nil, errs.WrapMsg(err, "GzipCompressor.DecompressWithPool: reading from pooled gzip reader failed")
|
||||
}
|
||||
if err = reader.Close(); err != nil {
|
||||
// Similar to DeCompress, return the data and error for close failure.
|
||||
return decompressedData, errs.Wrap(err, "GzipCompressor.DecompressWithPool: closing pooled gzip reader failed")
|
||||
return decompressedData, errs.WrapMsg(err, "GzipCompressor.DecompressWithPool: closing pooled gzip reader failed")
|
||||
}
|
||||
return decompressedData, nil
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ const (
|
||||
Compression = "compression"
|
||||
GzipCompressionProtocol = "gzip"
|
||||
BackgroundStatus = "isBackground"
|
||||
MsgResp = "isMsgResp"
|
||||
SendResponse = "isMsgResp"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -15,13 +15,16 @@
|
||||
package msggateway
|
||||
|
||||
import (
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
"github.com/openimsdk/tools/utils/encrypt"
|
||||
"github.com/openimsdk/tools/utils/stringutil"
|
||||
"github.com/openimsdk/tools/utils/timeutil"
|
||||
)
|
||||
|
||||
type UserConnContext struct {
|
||||
@@ -54,7 +57,7 @@ func (c *UserConnContext) Value(key any) any {
|
||||
case constant.ConnID:
|
||||
return c.GetConnID()
|
||||
case constant.OpUserPlatform:
|
||||
return constant.PlatformIDToName(utils.StringToInt(c.GetPlatformID()))
|
||||
return constant.PlatformIDToName(stringutil.StringToInt(c.GetPlatformID()))
|
||||
case constant.RemoteAddr:
|
||||
return c.RemoteAddr
|
||||
default:
|
||||
@@ -69,7 +72,7 @@ func newContext(respWriter http.ResponseWriter, req *http.Request) *UserConnCont
|
||||
Path: req.URL.Path,
|
||||
Method: req.Method,
|
||||
RemoteAddr: req.RemoteAddr,
|
||||
ConnID: utils.Md5(req.RemoteAddr + "_" + strconv.Itoa(int(utils.GetCurrentTimestampByMill()))),
|
||||
ConnID: encrypt.Md5(req.RemoteAddr + "_" + strconv.Itoa(int(timeutil.GetCurrentTimestampByMill()))),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,6 +136,32 @@ func (c *UserConnContext) GetToken() string {
|
||||
return c.Req.URL.Query().Get(Token)
|
||||
}
|
||||
|
||||
func (c *UserConnContext) GetCompression() bool {
|
||||
compression, exists := c.Query(Compression)
|
||||
if exists && compression == GzipCompressionProtocol {
|
||||
return true
|
||||
} else {
|
||||
compression, exists := c.GetHeader(Compression)
|
||||
if exists && compression == GzipCompressionProtocol {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *UserConnContext) ShouldSendResp() bool {
|
||||
errResp, exists := c.Query(SendResponse)
|
||||
if exists {
|
||||
b, err := strconv.ParseBool(errResp)
|
||||
if err != nil {
|
||||
return false
|
||||
} else {
|
||||
return b
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *UserConnContext) SetToken(token string) {
|
||||
c.Req.URL.RawQuery = Token + "=" + token
|
||||
}
|
||||
@@ -144,3 +173,23 @@ func (c *UserConnContext) GetBackground() bool {
|
||||
}
|
||||
return b
|
||||
}
|
||||
func (c *UserConnContext) ParseEssentialArgs() error {
|
||||
_, exists := c.Query(Token)
|
||||
if !exists {
|
||||
return servererrs.ErrConnArgsErr.WrapMsg("token is empty")
|
||||
}
|
||||
_, exists = c.Query(WsUserID)
|
||||
if !exists {
|
||||
return servererrs.ErrConnArgsErr.WrapMsg("sendID is empty")
|
||||
}
|
||||
platformIDStr, exists := c.Query(PlatformID)
|
||||
if !exists {
|
||||
return servererrs.ErrConnArgsErr.WrapMsg("platformID is empty")
|
||||
}
|
||||
_, err := strconv.Atoi(platformIDStr)
|
||||
if err != nil {
|
||||
return servererrs.ErrConnArgsErr.WrapMsg("platformID is not int")
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
)
|
||||
|
||||
type Encoder interface {
|
||||
@@ -35,9 +35,8 @@ func NewGobEncoder() *GobEncoder {
|
||||
func (g *GobEncoder) Encode(data any) ([]byte, error) {
|
||||
buff := bytes.Buffer{}
|
||||
enc := gob.NewEncoder(&buff)
|
||||
err := enc.Encode(data)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err, "GobEncoder.Encode failed")
|
||||
if err := enc.Encode(data); err != nil {
|
||||
return nil, errs.WrapMsg(err, "GobEncoder.Encode failed", "action", "encode")
|
||||
}
|
||||
return buff.Bytes(), nil
|
||||
}
|
||||
@@ -45,9 +44,8 @@ func (g *GobEncoder) Encode(data any) ([]byte, error) {
|
||||
func (g *GobEncoder) Decode(encodeData []byte, decodeData any) error {
|
||||
buff := bytes.NewBuffer(encodeData)
|
||||
dec := gob.NewDecoder(buff)
|
||||
err := dec.Decode(decodeData)
|
||||
if err != nil {
|
||||
return errs.Wrap(err, "GobEncoder.Decode failed")
|
||||
if err := dec.Decode(decodeData); err != nil {
|
||||
return errs.WrapMsg(err, "GobEncoder.Decode failed", "action", "decode")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -14,8 +14,12 @@
|
||||
|
||||
package msggateway
|
||||
|
||||
import "github.com/OpenIMSDK/tools/apiresp"
|
||||
import (
|
||||
"github.com/openimsdk/tools/apiresp"
|
||||
"github.com/openimsdk/tools/log"
|
||||
)
|
||||
|
||||
func httpError(ctx *UserConnContext, err error) {
|
||||
log.ZWarn(ctx, "ws connection error", err)
|
||||
apiresp.HttpError(ctx.RespWriter, err)
|
||||
}
|
||||
|
||||
@@ -16,38 +16,30 @@ package msggateway
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/OpenIMSDK/protocol/msggateway"
|
||||
"github.com/OpenIMSDK/tools/discoveryregistry"
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/OpenIMSDK/tools/mcontext"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/startrpc"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
"github.com/openimsdk/protocol/msggateway"
|
||||
"github.com/openimsdk/tools/discovery"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/mcontext"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
func (s *Server) InitServer(config *config.GlobalConfig, disCov discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error {
|
||||
rdb, err := cache.NewRedis(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msgModel := cache.NewMsgCacheModel(rdb, config)
|
||||
func (s *Server) InitServer(ctx context.Context, config *Config, disCov discovery.SvcDiscoveryRegistry, server *grpc.Server) error {
|
||||
s.LongConnServer.SetDiscoveryRegistry(disCov, config)
|
||||
s.LongConnServer.SetCacheHandler(msgModel)
|
||||
msggateway.RegisterMsgGatewayServer(server, s)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) Start(conf *config.GlobalConfig) error {
|
||||
return startrpc.Start(
|
||||
s.rpcPort,
|
||||
conf.RpcRegisterName.OpenImMessageGatewayName,
|
||||
s.prometheusPort,
|
||||
func (s *Server) Start(ctx context.Context, index int, conf *Config) error {
|
||||
return startrpc.Start(ctx, &conf.ZookeeperConfig, &conf.MsgGateway.Prometheus, conf.MsgGateway.ListenIP,
|
||||
conf.MsgGateway.RPC.RegisterIP,
|
||||
conf.MsgGateway.RPC.Ports, index,
|
||||
conf.Share.RpcRegisterName.MessageGateway,
|
||||
&conf.Share,
|
||||
conf,
|
||||
s.InitServer,
|
||||
)
|
||||
@@ -57,7 +49,7 @@ type Server struct {
|
||||
rpcPort int
|
||||
prometheusPort int
|
||||
LongConnServer LongConnServer
|
||||
config *config.GlobalConfig
|
||||
config *Config
|
||||
pushTerminal map[int]struct{}
|
||||
}
|
||||
|
||||
@@ -65,7 +57,7 @@ func (s *Server) SetLongConnServer(LongConnServer LongConnServer) {
|
||||
s.LongConnServer = LongConnServer
|
||||
}
|
||||
|
||||
func NewServer(rpcPort int, proPort int, longConnServer LongConnServer, conf *config.GlobalConfig) *Server {
|
||||
func NewServer(rpcPort int, proPort int, longConnServer LongConnServer, conf *Config) *Server {
|
||||
s := &Server{
|
||||
rpcPort: rpcPort,
|
||||
prometheusPort: proPort,
|
||||
@@ -89,8 +81,8 @@ func (s *Server) GetUsersOnlineStatus(
|
||||
ctx context.Context,
|
||||
req *msggateway.GetUsersOnlineStatusReq,
|
||||
) (*msggateway.GetUsersOnlineStatusResp, error) {
|
||||
if !authverify.IsAppManagerUid(ctx, s.config) {
|
||||
return nil, errs.ErrNoPermission.Wrap("only app manager")
|
||||
if !authverify.IsAppManagerUid(ctx, s.config.Share.IMAdminUserID) {
|
||||
return nil, errs.ErrNoPermission.WrapMsg("only app manager")
|
||||
}
|
||||
var resp msggateway.GetUsersOnlineStatusResp
|
||||
for _, userID := range req.UserIDs {
|
||||
@@ -122,11 +114,9 @@ func (s *Server) GetUsersOnlineStatus(
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (s *Server) OnlineBatchPushOneMsg(
|
||||
ctx context.Context,
|
||||
req *msggateway.OnlineBatchPushOneMsgReq,
|
||||
) (*msggateway.OnlineBatchPushOneMsgResp, error) {
|
||||
panic("implement me")
|
||||
func (s *Server) OnlineBatchPushOneMsg(ctx context.Context, req *msggateway.OnlineBatchPushOneMsgReq) (*msggateway.OnlineBatchPushOneMsgResp, error) {
|
||||
// todo implement
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *Server) SuperGroupOnlineBatchPushOneMsg(ctx context.Context, req *msggateway.OnlineBatchPushOneMsgReq,
|
||||
@@ -158,7 +148,7 @@ func (s *Server) SuperGroupOnlineBatchPushOneMsg(ctx context.Context, req *msgga
|
||||
(client.IsBackground && client.PlatformID != constant.IOSPlatformID) {
|
||||
err := client.PushMessage(ctx, req.MsgData)
|
||||
if err != nil {
|
||||
userPlatform.ResultCode = int64(errs.ErrPushMsgErr.Code())
|
||||
userPlatform.ResultCode = int64(servererrs.ErrPushMsgErr.Code())
|
||||
resp = append(resp, userPlatform)
|
||||
} else {
|
||||
if _, ok := s.pushTerminal[client.PlatformID]; ok {
|
||||
@@ -167,7 +157,7 @@ func (s *Server) SuperGroupOnlineBatchPushOneMsg(ctx context.Context, req *msgga
|
||||
}
|
||||
}
|
||||
} else {
|
||||
userPlatform.ResultCode = int64(errs.ErrIOSBackgroundPushErr.Code())
|
||||
userPlatform.ResultCode = int64(servererrs.ErrIOSBackgroundPushErr.Code())
|
||||
resp = append(resp, userPlatform)
|
||||
}
|
||||
}
|
||||
@@ -187,7 +177,7 @@ func (s *Server) KickUserOffline(
|
||||
for _, v := range req.KickUserIDList {
|
||||
clients, _, ok := s.LongConnServer.GetUserPlatformCons(v, int(req.PlatformID))
|
||||
if !ok {
|
||||
log.ZInfo(ctx, "conn not exist", "userID", v, "platformID", req.PlatformID)
|
||||
log.ZDebug(ctx, "conn not exist", "userID", v, "platformID", req.PlatformID)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -203,10 +193,7 @@ func (s *Server) KickUserOffline(
|
||||
return &msggateway.KickUserOfflineResp{}, nil
|
||||
}
|
||||
|
||||
func (s *Server) MultiTerminalLoginCheck(
|
||||
ctx context.Context,
|
||||
req *msggateway.MultiTerminalLoginCheckReq,
|
||||
) (*msggateway.MultiTerminalLoginCheckResp, error) {
|
||||
func (s *Server) MultiTerminalLoginCheck(ctx context.Context, req *msggateway.MultiTerminalLoginCheckReq) (*msggateway.MultiTerminalLoginCheckResp, error) {
|
||||
if oldClients, userOK, clientOK := s.LongConnServer.GetUserPlatformCons(req.UserID, int(req.PlatformID)); userOK {
|
||||
tempUserCtx := newTempContext()
|
||||
tempUserCtx.SetToken(req.Token)
|
||||
|
||||
+31
-10
@@ -15,22 +15,43 @@
|
||||
package msggateway
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"context"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/tools/log"
|
||||
)
|
||||
|
||||
// RunWsAndServer run ws server.
|
||||
func RunWsAndServer(conf *config.GlobalConfig, rpcPort, wsPort, prometheusPort int) error {
|
||||
fmt.Println("start rpc/msg_gateway server, port: ", rpcPort, wsPort, prometheusPort, ", OpenIM version: ", config.Version)
|
||||
type Config struct {
|
||||
MsgGateway config.MsgGateway
|
||||
ZookeeperConfig config.ZooKeeper
|
||||
Share config.Share
|
||||
WebhooksConfig config.Webhooks
|
||||
}
|
||||
|
||||
// Start run ws server.
|
||||
func Start(ctx context.Context, index int, conf *Config) error {
|
||||
log.CInfo(ctx, "MSG-GATEWAY server is initializing", "rpcPorts", conf.MsgGateway.RPC.Ports,
|
||||
"wsPort", conf.MsgGateway.LongConnSvr.Ports, "prometheusPorts", conf.MsgGateway.Prometheus.Ports)
|
||||
wsPort, err := datautil.GetElemByIndex(conf.MsgGateway.LongConnSvr.Ports, index)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
prometheusPort, err := datautil.GetElemByIndex(conf.MsgGateway.Prometheus.Ports, index)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rpcPort, err := datautil.GetElemByIndex(conf.MsgGateway.RPC.Ports, index)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
longServer, err := NewWsServer(
|
||||
conf,
|
||||
WithPort(wsPort),
|
||||
WithMaxConnNum(int64(conf.LongConnSvr.WebsocketMaxConnNum)),
|
||||
WithHandshakeTimeout(time.Duration(conf.LongConnSvr.WebsocketTimeout)*time.Second),
|
||||
WithMessageMaxMsgLength(conf.LongConnSvr.WebsocketMaxMsgLen),
|
||||
WithWriteBufferSize(conf.LongConnSvr.WebsocketWriteBufferSize),
|
||||
WithMaxConnNum(int64(conf.MsgGateway.LongConnSvr.WebsocketMaxConnNum)),
|
||||
WithHandshakeTimeout(time.Duration(conf.MsgGateway.LongConnSvr.WebsocketTimeout)*time.Second),
|
||||
WithMessageMaxMsgLength(conf.MsgGateway.LongConnSvr.WebsocketMaxMsgLen),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -39,7 +60,7 @@ func RunWsAndServer(conf *config.GlobalConfig, rpcPort, wsPort, prometheusPort i
|
||||
hubServer := NewServer(rpcPort, prometheusPort, longServer, conf)
|
||||
netDone := make(chan error)
|
||||
go func() {
|
||||
err = hubServer.Start(conf)
|
||||
err = hubServer.Start(ctx, index, conf)
|
||||
netDone <- err
|
||||
}()
|
||||
return hubServer.LongConnServer.Run(netDone)
|
||||
|
||||
@@ -15,12 +15,13 @@
|
||||
package msggateway
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"encoding/json"
|
||||
"github.com/openimsdk/tools/apiresp"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
)
|
||||
|
||||
type LongConn interface {
|
||||
@@ -75,7 +76,7 @@ func (d *GWebSocket) GenerateLongConn(w http.ResponseWriter, r *http.Request) er
|
||||
conn, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
// The upgrader.Upgrade method usually returns enough error messages to diagnose problems that may occur during the upgrade
|
||||
return errs.Wrap(err, "GenerateLongConn: WebSocket upgrade failed")
|
||||
return errs.WrapMsg(err, "GenerateLongConn: WebSocket upgrade failed")
|
||||
}
|
||||
d.conn = conn
|
||||
return nil
|
||||
@@ -86,7 +87,7 @@ func (d *GWebSocket) WriteMessage(messageType int, message []byte) error {
|
||||
return d.conn.WriteMessage(messageType, message)
|
||||
}
|
||||
|
||||
//func (d *GWebSocket) setSendConn(sendConn *websocket.Conn) {
|
||||
// func (d *GWebSocket) setSendConn(sendConn *websocket.Conn) {
|
||||
// d.sendConn = sendConn
|
||||
//}
|
||||
|
||||
@@ -99,24 +100,24 @@ func (d *GWebSocket) SetReadDeadline(timeout time.Duration) error {
|
||||
}
|
||||
|
||||
func (d *GWebSocket) SetWriteDeadline(timeout time.Duration) error {
|
||||
// TODO add error
|
||||
if timeout <= 0 {
|
||||
return errs.Wrap(errors.New("timeout must be greater than 0"))
|
||||
return errs.New("timeout must be greater than 0")
|
||||
}
|
||||
|
||||
// TODO SetWriteDeadline Future add error handling
|
||||
if err := d.conn.SetWriteDeadline(time.Now().Add(timeout)); err != nil {
|
||||
return errs.Wrap(err, "GWebSocket.SetWriteDeadline failed")
|
||||
return errs.WrapMsg(err, "GWebSocket.SetWriteDeadline failed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *GWebSocket) Dial(urlStr string, requestHeader http.Header) (*http.Response, error) {
|
||||
conn, httpResp, err := websocket.DefaultDialer.Dial(urlStr, requestHeader)
|
||||
if err == nil {
|
||||
d.conn = conn
|
||||
if err != nil {
|
||||
return httpResp, errs.WrapMsg(err, "GWebSocket.Dial failed", "url", urlStr)
|
||||
}
|
||||
return httpResp, err
|
||||
d.conn = conn
|
||||
return httpResp, nil
|
||||
}
|
||||
|
||||
func (d *GWebSocket) IsNil() bool {
|
||||
@@ -144,6 +145,34 @@ func (d *GWebSocket) SetPingHandler(handler PingPongHandler) {
|
||||
d.conn.SetPingHandler(handler)
|
||||
}
|
||||
|
||||
//func (d *GWebSocket) CheckSendConnDiffNow() bool {
|
||||
// return d.conn == d.sendConn
|
||||
//}
|
||||
func (d *GWebSocket) RespondWithError(err error, w http.ResponseWriter, r *http.Request) error {
|
||||
if err := d.GenerateLongConn(w, r); err != nil {
|
||||
return err
|
||||
}
|
||||
data, err := json.Marshal(apiresp.ParseError(err))
|
||||
if err != nil {
|
||||
_ = d.Close()
|
||||
return errs.WrapMsg(err, "json marshal failed")
|
||||
}
|
||||
|
||||
if err := d.WriteMessage(MessageText, data); err != nil {
|
||||
_ = d.Close()
|
||||
return errs.WrapMsg(err, "WriteMessage failed")
|
||||
}
|
||||
_ = d.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *GWebSocket) RespondWithSuccess() error {
|
||||
data, err := json.Marshal(apiresp.ParseError(nil))
|
||||
if err != nil {
|
||||
_ = d.Close()
|
||||
return errs.WrapMsg(err, "json marshal failed")
|
||||
}
|
||||
|
||||
if err := d.WriteMessage(MessageText, data); err != nil {
|
||||
_ = d.Close()
|
||||
return errs.WrapMsg(err, "WriteMessage failed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -18,15 +18,15 @@ import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/msg"
|
||||
"github.com/OpenIMSDK/protocol/push"
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
"github.com/OpenIMSDK/tools/discoveryregistry"
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
"github.com/openimsdk/protocol/msg"
|
||||
"github.com/openimsdk/protocol/push"
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
"github.com/openimsdk/tools/discovery"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/utils/jsonutil"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
@@ -46,7 +46,7 @@ func (r *Req) String() string {
|
||||
tReq.SendID = r.SendID
|
||||
tReq.OperationID = r.OperationID
|
||||
tReq.MsgIncr = r.MsgIncr
|
||||
return utils.StructToJsonString(tReq)
|
||||
return jsonutil.StructToJsonString(tReq)
|
||||
}
|
||||
|
||||
var reqPool = sync.Pool{
|
||||
@@ -86,7 +86,7 @@ func (r *Resp) String() string {
|
||||
tResp.OperationID = r.OperationID
|
||||
tResp.ErrCode = r.ErrCode
|
||||
tResp.ErrMsg = r.ErrMsg
|
||||
return utils.StructToJsonString(tResp)
|
||||
return jsonutil.StructToJsonString(tResp)
|
||||
}
|
||||
|
||||
type MessageHandler interface {
|
||||
@@ -106,30 +106,30 @@ type GrpcHandler struct {
|
||||
validate *validator.Validate
|
||||
}
|
||||
|
||||
func NewGrpcHandler(validate *validator.Validate, client discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig) *GrpcHandler {
|
||||
msgRpcClient := rpcclient.NewMessageRpcClient(client, config)
|
||||
pushRpcClient := rpcclient.NewPushRpcClient(client, config)
|
||||
func NewGrpcHandler(validate *validator.Validate, client discovery.SvcDiscoveryRegistry, rpcRegisterName *config.RpcRegisterName) *GrpcHandler {
|
||||
msgRpcClient := rpcclient.NewMessageRpcClient(client, rpcRegisterName.Msg)
|
||||
pushRpcClient := rpcclient.NewPushRpcClient(client, rpcRegisterName.Push)
|
||||
return &GrpcHandler{
|
||||
msgRpcClient: &msgRpcClient,
|
||||
pushClient: &pushRpcClient, validate: validate,
|
||||
}
|
||||
}
|
||||
|
||||
func (g GrpcHandler) GetSeq(context context.Context, data *Req) ([]byte, error) {
|
||||
func (g GrpcHandler) GetSeq(ctx context.Context, data *Req) ([]byte, error) {
|
||||
req := sdkws.GetMaxSeqReq{}
|
||||
if err := proto.Unmarshal(data.Data, &req); err != nil {
|
||||
return nil, errs.Wrap(err, "GetSeq: error unmarshaling request")
|
||||
return nil, errs.WrapMsg(err, "GetSeq: error unmarshaling request", "action", "unmarshal", "dataType", "GetMaxSeqReq")
|
||||
}
|
||||
if err := g.validate.Struct(&req); err != nil {
|
||||
return nil, errs.Wrap(err, "GetSeq: validation failed")
|
||||
return nil, errs.WrapMsg(err, "GetSeq: validation failed", "action", "validate", "dataType", "GetMaxSeqReq")
|
||||
}
|
||||
resp, err := g.msgRpcClient.GetMaxSeq(context, &req)
|
||||
resp, err := g.msgRpcClient.GetMaxSeq(ctx, &req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c, err := proto.Marshal(resp)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err, "GetSeq: error marshaling response")
|
||||
return nil, errs.WrapMsg(err, "GetSeq: error marshaling response", "action", "marshal", "dataType", "GetMaxSeqResp")
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
@@ -137,19 +137,16 @@ func (g GrpcHandler) GetSeq(context context.Context, data *Req) ([]byte, error)
|
||||
// SendMessage handles the sending of messages through gRPC. It unmarshals the request data,
|
||||
// validates the message, and then sends it using the message RPC client.
|
||||
func (g GrpcHandler) SendMessage(ctx context.Context, data *Req) ([]byte, error) {
|
||||
// Unmarshal the message data from the request.
|
||||
var msgData sdkws.MsgData
|
||||
if err := proto.Unmarshal(data.Data, &msgData); err != nil {
|
||||
return nil, errs.Wrap(err, "error unmarshalling message data")
|
||||
return nil, errs.WrapMsg(err, "SendMessage: error unmarshaling message data", "action", "unmarshal", "dataType", "MsgData")
|
||||
}
|
||||
|
||||
// Validate the message data structure.
|
||||
if err := g.validate.Struct(&msgData); err != nil {
|
||||
return nil, errs.Wrap(err, "message data validation failed")
|
||||
return nil, errs.WrapMsg(err, "SendMessage: message data validation failed", "action", "validate", "dataType", "MsgData")
|
||||
}
|
||||
|
||||
req := msg.SendMsgReq{MsgData: &msgData}
|
||||
|
||||
resp, err := g.msgRpcClient.SendMsg(ctx, &req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -157,7 +154,7 @@ func (g GrpcHandler) SendMessage(ctx context.Context, data *Req) ([]byte, error)
|
||||
|
||||
c, err := proto.Marshal(resp)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err, "error marshaling response")
|
||||
return nil, errs.WrapMsg(err, "SendMessage: error marshaling response", "action", "marshal", "dataType", "SendMsgResp")
|
||||
}
|
||||
|
||||
return c, nil
|
||||
@@ -170,7 +167,7 @@ func (g GrpcHandler) SendSignalMessage(context context.Context, data *Req) ([]by
|
||||
}
|
||||
c, err := proto.Marshal(resp)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err, "error marshaling response")
|
||||
return nil, errs.WrapMsg(err, "error marshaling response", "action", "marshal", "dataType", "SendMsgResp")
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
@@ -178,10 +175,10 @@ func (g GrpcHandler) SendSignalMessage(context context.Context, data *Req) ([]by
|
||||
func (g GrpcHandler) PullMessageBySeqList(context context.Context, data *Req) ([]byte, error) {
|
||||
req := sdkws.PullMessageBySeqsReq{}
|
||||
if err := proto.Unmarshal(data.Data, &req); err != nil {
|
||||
return nil, errs.Wrap(err, "error unmarshaling request")
|
||||
return nil, errs.WrapMsg(err, "error unmarshaling request", "action", "unmarshal", "dataType", "PullMessageBySeqsReq")
|
||||
}
|
||||
if err := g.validate.Struct(data); err != nil {
|
||||
return nil, errs.Wrap(err, "validation failed")
|
||||
return nil, errs.WrapMsg(err, "validation failed", "action", "validate", "dataType", "PullMessageBySeqsReq")
|
||||
}
|
||||
resp, err := g.msgRpcClient.PullMessageBySeqList(context, &req)
|
||||
if err != nil {
|
||||
@@ -189,7 +186,7 @@ func (g GrpcHandler) PullMessageBySeqList(context context.Context, data *Req) ([
|
||||
}
|
||||
c, err := proto.Marshal(resp)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err, "error marshaling response")
|
||||
return nil, errs.WrapMsg(err, "error marshaling response", "action", "marshal", "dataType", "PullMessageBySeqsResp")
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
@@ -197,7 +194,7 @@ func (g GrpcHandler) PullMessageBySeqList(context context.Context, data *Req) ([
|
||||
func (g GrpcHandler) UserLogout(context context.Context, data *Req) ([]byte, error) {
|
||||
req := push.DelUserPushTokenReq{}
|
||||
if err := proto.Unmarshal(data.Data, &req); err != nil {
|
||||
return nil, errs.Wrap(err, "error unmarshaling request")
|
||||
return nil, errs.WrapMsg(err, "error unmarshaling request", "action", "unmarshal", "dataType", "DelUserPushTokenReq")
|
||||
}
|
||||
resp, err := g.pushClient.DelUserPushToken(context, &req)
|
||||
if err != nil {
|
||||
@@ -205,7 +202,7 @@ func (g GrpcHandler) UserLogout(context context.Context, data *Req) ([]byte, err
|
||||
}
|
||||
c, err := proto.Marshal(resp)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err, "error marshaling response")
|
||||
return nil, errs.WrapMsg(err, "error marshaling response", "action", "marshal", "dataType", "DelUserPushTokenResp")
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
@@ -213,31 +210,10 @@ func (g GrpcHandler) UserLogout(context context.Context, data *Req) ([]byte, err
|
||||
func (g GrpcHandler) SetUserDeviceBackground(_ context.Context, data *Req) ([]byte, bool, error) {
|
||||
req := sdkws.SetAppBackgroundStatusReq{}
|
||||
if err := proto.Unmarshal(data.Data, &req); err != nil {
|
||||
return nil, false, errs.Wrap(err, "error unmarshaling request")
|
||||
return nil, false, errs.WrapMsg(err, "error unmarshaling request", "action", "unmarshal", "dataType", "SetAppBackgroundStatusReq")
|
||||
}
|
||||
if err := g.validate.Struct(data); err != nil {
|
||||
return nil, false, errs.Wrap(err, "validation failed")
|
||||
return nil, false, errs.WrapMsg(err, "validation failed", "action", "validate", "dataType", "SetAppBackgroundStatusReq")
|
||||
}
|
||||
return nil, req.IsBackground, nil
|
||||
}
|
||||
|
||||
// func (g GrpcHandler) call[T any](ctx context.Context, data Req, m proto.Message, rpc func(ctx context.Context, req
|
||||
// proto.Message)) ([]byte, error) {
|
||||
// if err := proto.Unmarshal(data.Data, m); err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// if err := g.validate.Struct(m); err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// rpc(ctx, m)
|
||||
// req := msg.SendMsgReq{MsgData: &msgData}
|
||||
// resp, err := g.notification.Msg.SendMsg(context, &req)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// c, err := proto.Marshal(resp)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// return c, nil
|
||||
//}
|
||||
|
||||
+114
-191
@@ -16,29 +16,25 @@ package msggateway
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/webhook"
|
||||
pbAuth "github.com/openimsdk/protocol/auth"
|
||||
"github.com/openimsdk/tools/mcontext"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/OpenIMSDK/protocol/msggateway"
|
||||
"github.com/OpenIMSDK/tools/apiresp"
|
||||
"github.com/OpenIMSDK/tools/discoveryregistry"
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
"github.com/openimsdk/protocol/msggateway"
|
||||
"github.com/openimsdk/tools/discovery"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/utils/stringutil"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
@@ -48,8 +44,7 @@ type LongConnServer interface {
|
||||
GetUserAllCons(userID string) ([]*Client, bool)
|
||||
GetUserPlatformCons(userID string, platform int) ([]*Client, bool, bool)
|
||||
Validate(s any) error
|
||||
SetCacheHandler(cache cache.MsgModel)
|
||||
SetDiscoveryRegistry(client discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig)
|
||||
SetDiscoveryRegistry(client discovery.SvcDiscoveryRegistry, config *Config)
|
||||
KickUserConn(client *Client) error
|
||||
UnRegister(c *Client)
|
||||
SetKickHandlerInfo(i *kickHandler)
|
||||
@@ -58,15 +53,8 @@ type LongConnServer interface {
|
||||
MessageHandler
|
||||
}
|
||||
|
||||
// bufferPool is unused
|
||||
// var bufferPool = sync.Pool{
|
||||
// New: func() any {
|
||||
// return make([]byte, 1024)
|
||||
// },
|
||||
// }
|
||||
|
||||
type WsServer struct {
|
||||
globalConfig *config.GlobalConfig
|
||||
msgGatewayConfig *Config
|
||||
port int
|
||||
wsMaxConnNum int64
|
||||
registerChan chan *Client
|
||||
@@ -79,12 +67,13 @@ type WsServer struct {
|
||||
handshakeTimeout time.Duration
|
||||
writeBufferSize int
|
||||
validate *validator.Validate
|
||||
cache cache.MsgModel
|
||||
userClient *rpcclient.UserRpcClient
|
||||
disCov discoveryregistry.SvcDiscoveryRegistry
|
||||
authClient *rpcclient.Auth
|
||||
disCov discovery.SvcDiscoveryRegistry
|
||||
Compressor
|
||||
Encoder
|
||||
MessageHandler
|
||||
webhookClient *webhook.Client
|
||||
}
|
||||
|
||||
type kickHandler struct {
|
||||
@@ -93,9 +82,10 @@ type kickHandler struct {
|
||||
newClient *Client
|
||||
}
|
||||
|
||||
func (ws *WsServer) SetDiscoveryRegistry(disCov discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig) {
|
||||
ws.MessageHandler = NewGrpcHandler(ws.validate, disCov, config)
|
||||
u := rpcclient.NewUserRpcClient(disCov, config)
|
||||
func (ws *WsServer) SetDiscoveryRegistry(disCov discovery.SvcDiscoveryRegistry, config *Config) {
|
||||
ws.MessageHandler = NewGrpcHandler(ws.validate, disCov, &config.Share.RpcRegisterName)
|
||||
u := rpcclient.NewUserRpcClient(disCov, config.Share.RpcRegisterName.User, config.Share.IMAdminUserID)
|
||||
ws.authClient = rpcclient.NewAuth(disCov, config.Share.RpcRegisterName.Auth)
|
||||
ws.userClient = &u
|
||||
ws.disCov = disCov
|
||||
}
|
||||
@@ -107,30 +97,17 @@ func (ws *WsServer) SetUserOnlineStatus(ctx context.Context, client *Client, sta
|
||||
}
|
||||
switch status {
|
||||
case constant.Online:
|
||||
err := CallbackUserOnline(ctx, ws.globalConfig, client.UserID, client.PlatformID, client.IsBackground, client.ctx.GetConnID())
|
||||
if err != nil {
|
||||
log.ZWarn(ctx, "CallbackUserOnline err", err)
|
||||
}
|
||||
ws.webhookAfterUserOnline(ctx, &ws.msgGatewayConfig.WebhooksConfig.AfterUserOnline, client.UserID, client.PlatformID, client.IsBackground, client.ctx.GetConnID())
|
||||
case constant.Offline:
|
||||
err := CallbackUserOffline(ctx, ws.globalConfig, client.UserID, client.PlatformID, client.ctx.GetConnID())
|
||||
if err != nil {
|
||||
log.ZWarn(ctx, "CallbackUserOffline err", err)
|
||||
}
|
||||
ws.webhookAfterUserOffline(ctx, &ws.msgGatewayConfig.WebhooksConfig.AfterUserOffline, client.UserID, client.PlatformID, client.ctx.GetConnID())
|
||||
}
|
||||
}
|
||||
|
||||
func (ws *WsServer) SetCacheHandler(cache cache.MsgModel) {
|
||||
ws.cache = cache
|
||||
}
|
||||
|
||||
func (ws *WsServer) UnRegister(c *Client) {
|
||||
ws.unregisterChan <- c
|
||||
}
|
||||
|
||||
func (ws *WsServer) Validate(s any) error {
|
||||
if s == nil {
|
||||
return errs.Wrap(errors.New("input cannot be nil"))
|
||||
}
|
||||
func (ws *WsServer) Validate(_ any) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -142,14 +119,14 @@ func (ws *WsServer) GetUserPlatformCons(userID string, platform int) ([]*Client,
|
||||
return ws.clients.Get(userID, platform)
|
||||
}
|
||||
|
||||
func NewWsServer(globalConfig *config.GlobalConfig, opts ...Option) (*WsServer, error) {
|
||||
func NewWsServer(msgGatewayConfig *Config, opts ...Option) (*WsServer, error) {
|
||||
var config configs
|
||||
for _, o := range opts {
|
||||
o(&config)
|
||||
}
|
||||
v := validator.New()
|
||||
return &WsServer{
|
||||
globalConfig: globalConfig,
|
||||
msgGatewayConfig: msgGatewayConfig,
|
||||
port: config.port,
|
||||
wsMaxConnNum: config.maxConnNum,
|
||||
writeBufferSize: config.writeBufferSize,
|
||||
@@ -166,6 +143,7 @@ func NewWsServer(globalConfig *config.GlobalConfig, opts ...Option) (*WsServer,
|
||||
clients: newUserMap(),
|
||||
Compressor: NewGzipCompressor(),
|
||||
Encoder: NewGobEncoder(),
|
||||
webhookClient: webhook.NewWebhookClient(msgGatewayConfig.WebhooksConfig.URL),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -176,7 +154,7 @@ func (ws *WsServer) Run(done chan error) error {
|
||||
shutdownDone = make(chan struct{}, 1)
|
||||
)
|
||||
|
||||
server := http.Server{Addr: ":" + utils.IntToString(ws.port), Handler: nil}
|
||||
server := http.Server{Addr: ":" + stringutil.IntToString(ws.port), Handler: nil}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
@@ -196,9 +174,9 @@ func (ws *WsServer) Run(done chan error) error {
|
||||
go func() {
|
||||
http.HandleFunc("/", ws.wsHandler)
|
||||
err := server.ListenAndServe()
|
||||
defer close(netDone)
|
||||
if err != nil && err != http.ErrServerClosed {
|
||||
netErr = errs.Wrap(err, "ws start err", server.Addr)
|
||||
close(netDone)
|
||||
netErr = errs.WrapMsg(err, "ws start err", server.Addr)
|
||||
}
|
||||
}()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
@@ -208,7 +186,7 @@ func (ws *WsServer) Run(done chan error) error {
|
||||
case err = <-done:
|
||||
sErr := server.Shutdown(ctx)
|
||||
if sErr != nil {
|
||||
return errs.Wrap(sErr, "shutdown err")
|
||||
return errs.WrapMsg(sErr, "shutdown err")
|
||||
}
|
||||
close(shutdownDone)
|
||||
if err != nil {
|
||||
@@ -223,7 +201,7 @@ func (ws *WsServer) Run(done chan error) error {
|
||||
var concurrentRequest = 3
|
||||
|
||||
func (ws *WsServer) sendUserOnlineInfoToOtherNode(ctx context.Context, client *Client) error {
|
||||
conns, err := ws.disCov.GetConns(ctx, ws.globalConfig.RpcRegisterName.OpenImMessageGatewayName)
|
||||
conns, err := ws.disCov.GetConns(ctx, ws.msgGatewayConfig.Share.RpcRegisterName.MessageGateway)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -279,7 +257,8 @@ func (ws *WsServer) registerClient(client *Client) {
|
||||
if clientOK {
|
||||
ws.clients.Set(client.UserID, client)
|
||||
// There is already a connection to the platform
|
||||
log.ZInfo(client.ctx, "repeat login", "userID", client.UserID, "platformID", client.PlatformID, "old remote addr", getRemoteAdders(oldClients))
|
||||
log.ZInfo(client.ctx, "repeat login", "userID", client.UserID, "platformID",
|
||||
client.PlatformID, "old remote addr", getRemoteAdders(oldClients))
|
||||
ws.onlineUserConnNum.Add(1)
|
||||
} else {
|
||||
ws.clients.Set(client.UserID, client)
|
||||
@@ -288,13 +267,14 @@ func (ws *WsServer) registerClient(client *Client) {
|
||||
}
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
if ws.globalConfig.Envs.Discovery == "zookeeper" {
|
||||
if ws.msgGatewayConfig.Share.Env == "zookeeper" {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
_ = ws.sendUserOnlineInfoToOtherNode(client.ctx, client)
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
@@ -331,7 +311,7 @@ func (ws *WsServer) KickUserConn(client *Client) error {
|
||||
}
|
||||
|
||||
func (ws *WsServer) multiTerminalLoginChecker(clientOK bool, oldClients []*Client, newClient *Client) {
|
||||
switch ws.globalConfig.MultiLoginPolicy {
|
||||
switch ws.msgGatewayConfig.MsgGateway.MultiLoginPolicy {
|
||||
case constant.DefalutNotKick:
|
||||
case constant.PCAndOther:
|
||||
if constant.PlatformIDToClass(newClient.PlatformID) == constant.TerminalPC {
|
||||
@@ -349,57 +329,13 @@ func (ws *WsServer) multiTerminalLoginChecker(clientOK bool, oldClients []*Clien
|
||||
log.ZWarn(c.ctx, "KickOnlineMessage", err)
|
||||
}
|
||||
}
|
||||
m, err := ws.cache.GetTokensWithoutError(
|
||||
newClient.ctx,
|
||||
newClient.UserID,
|
||||
newClient.PlatformID,
|
||||
ctx := mcontext.WithMustInfoCtx(
|
||||
[]string{newClient.ctx.GetOperationID(), newClient.ctx.GetUserID(),
|
||||
constant.PlatformIDToName(newClient.PlatformID), newClient.ctx.GetConnID()},
|
||||
)
|
||||
if err != nil && err != redis.Nil {
|
||||
log.ZWarn(
|
||||
newClient.ctx,
|
||||
"get token from redis err",
|
||||
err,
|
||||
"userID",
|
||||
newClient.UserID,
|
||||
"platformID",
|
||||
newClient.PlatformID,
|
||||
)
|
||||
return
|
||||
}
|
||||
if m == nil {
|
||||
log.ZWarn(
|
||||
newClient.ctx,
|
||||
"m is nil",
|
||||
errors.New("m is nil"),
|
||||
"userID",
|
||||
newClient.UserID,
|
||||
"platformID",
|
||||
newClient.PlatformID,
|
||||
)
|
||||
return
|
||||
}
|
||||
log.ZDebug(
|
||||
newClient.ctx,
|
||||
"get token from redis",
|
||||
"userID",
|
||||
newClient.UserID,
|
||||
"platformID",
|
||||
newClient.PlatformID,
|
||||
"tokenMap",
|
||||
m,
|
||||
)
|
||||
|
||||
for k := range m {
|
||||
if k != newClient.ctx.GetToken() {
|
||||
m[k] = constant.KickedToken
|
||||
}
|
||||
}
|
||||
log.ZDebug(newClient.ctx, "set token map is ", "token map", m, "userID",
|
||||
newClient.UserID, "token", newClient.ctx.GetToken())
|
||||
err = ws.cache.SetTokenMapByUidPid(newClient.ctx, newClient.UserID, newClient.PlatformID, m)
|
||||
if err != nil {
|
||||
log.ZWarn(newClient.ctx, "SetTokenMapByUidPid err", err, "userID", newClient.UserID, "platformID", newClient.PlatformID)
|
||||
return
|
||||
if _, err := ws.authClient.InvalidateToken(ctx, newClient.token, newClient.UserID, newClient.PlatformID); err != nil {
|
||||
log.ZWarn(newClient.ctx, "InvalidateToken err", err, "userID", newClient.UserID,
|
||||
"platformID", newClient.PlatformID)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -413,107 +349,94 @@ func (ws *WsServer) unregisterClient(client *Client) {
|
||||
}
|
||||
ws.onlineUserConnNum.Add(-1)
|
||||
ws.SetUserOnlineStatus(client.ctx, client, constant.Offline)
|
||||
log.ZInfo(client.ctx, "user offline", "close reason", client.closedErr, "online user Num", ws.onlineUserNum.Load(), "online user conn Num",
|
||||
log.ZInfo(client.ctx, "user offline", "close reason", client.closedErr, "online user Num",
|
||||
ws.onlineUserNum.Load(), "online user conn Num",
|
||||
ws.onlineUserConnNum.Load(),
|
||||
)
|
||||
}
|
||||
|
||||
func (ws *WsServer) ParseWSArgs(r *http.Request) (args *WSArgs, err error) {
|
||||
var v WSArgs
|
||||
defer func() {
|
||||
args = &v
|
||||
}()
|
||||
query := r.URL.Query()
|
||||
v.MsgResp, _ = strconv.ParseBool(query.Get(MsgResp))
|
||||
if ws.onlineUserConnNum.Load() >= ws.wsMaxConnNum {
|
||||
return nil, errs.ErrConnOverMaxNumLimit.Wrap("over max conn num limit")
|
||||
// validateRespWithRequest checks if the response matches the expected userID and platformID.
|
||||
func (ws *WsServer) validateRespWithRequest(ctx *UserConnContext, resp *pbAuth.ParseTokenResp) error {
|
||||
userID := ctx.GetUserID()
|
||||
platformID := stringutil.StringToInt32(ctx.GetPlatformID())
|
||||
if resp.UserID != userID {
|
||||
return servererrs.ErrTokenInvalid.WrapMsg(fmt.Sprintf("token uid %s != userID %s", resp.UserID, userID))
|
||||
}
|
||||
if v.Token = query.Get(Token); v.Token == "" {
|
||||
return nil, errs.ErrConnArgsErr.Wrap("token is empty")
|
||||
if resp.PlatformID != platformID {
|
||||
return servererrs.ErrTokenInvalid.WrapMsg(fmt.Sprintf("token platform %d != platformID %d", resp.PlatformID, platformID))
|
||||
}
|
||||
if v.UserID = query.Get(WsUserID); v.UserID == "" {
|
||||
return nil, errs.ErrConnArgsErr.Wrap("sendID is empty")
|
||||
}
|
||||
platformIDStr := query.Get(PlatformID)
|
||||
if platformIDStr == "" {
|
||||
return nil, errs.ErrConnArgsErr.Wrap("platformID is empty")
|
||||
}
|
||||
platformID, err := strconv.Atoi(platformIDStr)
|
||||
if err != nil {
|
||||
return nil, errs.ErrConnArgsErr.Wrap("platformID is not int")
|
||||
}
|
||||
v.PlatformID = platformID
|
||||
if err = authverify.WsVerifyToken(v.Token, v.UserID, ws.globalConfig.Secret, platformID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if query.Get(Compression) == GzipCompressionProtocol {
|
||||
v.Compression = true
|
||||
}
|
||||
if r.Header.Get(Compression) == GzipCompressionProtocol {
|
||||
v.Compression = true
|
||||
}
|
||||
m, err := ws.cache.GetTokensWithoutError(context.Background(), v.UserID, platformID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if v, ok := m[v.Token]; ok {
|
||||
switch v {
|
||||
case constant.NormalToken:
|
||||
case constant.KickedToken:
|
||||
return nil, errs.ErrTokenKicked.Wrap()
|
||||
default:
|
||||
return nil, errs.ErrTokenUnknown.Wrap(fmt.Sprintf("token status is %d", v))
|
||||
}
|
||||
} else {
|
||||
return nil, errs.ErrTokenNotExist.Wrap()
|
||||
}
|
||||
return &v, nil
|
||||
}
|
||||
|
||||
type WSArgs struct {
|
||||
Token string
|
||||
UserID string
|
||||
PlatformID int
|
||||
Compression bool
|
||||
MsgResp bool
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ws *WsServer) wsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// Create a new connection context
|
||||
connContext := newContext(w, r)
|
||||
args, pErr := ws.ParseWSArgs(r)
|
||||
var wsLongConn *GWebSocket
|
||||
if args.MsgResp {
|
||||
wsLongConn = newGWebSocket(WebSocket, ws.handshakeTimeout, ws.writeBufferSize)
|
||||
if err := wsLongConn.GenerateLongConn(w, r); err != nil {
|
||||
httpError(connContext, err)
|
||||
return
|
||||
}
|
||||
data, err := json.Marshal(apiresp.ParseError(pErr))
|
||||
if err != nil {
|
||||
_ = wsLongConn.Close()
|
||||
return
|
||||
}
|
||||
if err := wsLongConn.WriteMessage(MessageText, data); err != nil {
|
||||
_ = wsLongConn.Close()
|
||||
return
|
||||
}
|
||||
if pErr != nil {
|
||||
_ = wsLongConn.Close()
|
||||
return
|
||||
|
||||
// Check if the current number of online user connections exceeds the maximum limit
|
||||
if ws.onlineUserConnNum.Load() >= ws.wsMaxConnNum {
|
||||
// If it exceeds the maximum connection number, return an error via HTTP and stop processing
|
||||
httpError(connContext, servererrs.ErrConnOverMaxNumLimit.WrapMsg("over max conn num limit"))
|
||||
return
|
||||
}
|
||||
|
||||
// Parse essential arguments (e.g., user ID, Token)
|
||||
err := connContext.ParseEssentialArgs()
|
||||
if err != nil {
|
||||
// If there's an error during parsing, return an error via HTTP and stop processing
|
||||
|
||||
httpError(connContext, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Call the authentication client to parse the Token obtained from the context
|
||||
resp, err := ws.authClient.ParseToken(connContext, connContext.GetToken())
|
||||
if err != nil {
|
||||
// If there's an error parsing the Token, decide whether to send the error message via WebSocket based on the context flag
|
||||
shouldSendError := connContext.ShouldSendResp()
|
||||
if shouldSendError {
|
||||
// Create a WebSocket connection object and attempt to send the error message via WebSocket
|
||||
wsLongConn := newGWebSocket(WebSocket, ws.handshakeTimeout, ws.writeBufferSize)
|
||||
if err := wsLongConn.RespondWithError(err, w, r); err == nil {
|
||||
// If the error message is successfully sent via WebSocket, stop processing
|
||||
return
|
||||
}
|
||||
}
|
||||
// If sending via WebSocket is not required or fails, return the error via HTTP and stop processing
|
||||
httpError(connContext, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Validate the authentication response matches the request (e.g., user ID and platform ID)
|
||||
err = ws.validateRespWithRequest(connContext, resp)
|
||||
if err != nil {
|
||||
// If validation fails, return an error via HTTP and stop processing
|
||||
httpError(connContext, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Create a WebSocket long connection object
|
||||
wsLongConn := newGWebSocket(WebSocket, ws.handshakeTimeout, ws.writeBufferSize)
|
||||
if err := wsLongConn.GenerateLongConn(w, r); err != nil {
|
||||
//If the creation of the long connection fails, the error is handled internally during the handshake process.
|
||||
log.ZWarn(connContext, "long connection fails", err)
|
||||
return
|
||||
} else {
|
||||
if pErr != nil {
|
||||
httpError(connContext, pErr)
|
||||
return
|
||||
}
|
||||
wsLongConn = newGWebSocket(WebSocket, ws.handshakeTimeout, ws.writeBufferSize)
|
||||
if err := wsLongConn.GenerateLongConn(w, r); err != nil {
|
||||
httpError(connContext, err)
|
||||
return
|
||||
// Check if a normal response should be sent via WebSocket
|
||||
shouldSendSuccessResp := connContext.ShouldSendResp()
|
||||
if shouldSendSuccessResp {
|
||||
// Attempt to send a success message through WebSocket
|
||||
if err := wsLongConn.RespondWithSuccess(); err != nil {
|
||||
// If the success message is successfully sent, end further processing
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve a client object from the client pool, reset its state, and associate it with the current WebSocket long connection
|
||||
client := ws.clientPool.Get().(*Client)
|
||||
client.ResetClient(connContext, wsLongConn, connContext.GetBackground(), args.Compression, ws, args.Token)
|
||||
client.ResetClient(connContext, wsLongConn, ws)
|
||||
|
||||
// Register the client with the server and start message processing
|
||||
ws.registerChan <- client
|
||||
go client.readMessage()
|
||||
}
|
||||
|
||||
@@ -18,7 +18,8 @@ import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
)
|
||||
|
||||
type UserMap struct {
|
||||
@@ -54,6 +55,7 @@ func (u *UserMap) Get(key string, platformID int) ([]*Client, bool, bool) {
|
||||
return nil, userExisted, false
|
||||
}
|
||||
|
||||
// Set adds a client to the map.
|
||||
func (u *UserMap) Set(key string, v *Client) {
|
||||
allClients, existed := u.m.Load(key)
|
||||
if existed {
|
||||
@@ -63,6 +65,7 @@ func (u *UserMap) Set(key string, v *Client) {
|
||||
u.m.Store(key, oldClients)
|
||||
} else {
|
||||
log.ZDebug(context.Background(), "Set not existed", "user_id", key, "client_user_id", v.UserID)
|
||||
|
||||
var clients []*Client
|
||||
clients = append(clients, v)
|
||||
u.m.Store(key, clients)
|
||||
@@ -98,14 +101,10 @@ func (u *UserMap) delete(key string, connRemoteAddr string) (isDeleteUser bool)
|
||||
return false
|
||||
}
|
||||
|
||||
func (u *UserMap) deleteClients(key string, clientsToDelete []*Client) (isDeleteUser bool) {
|
||||
// Convert the slice of clients to delete into a map for efficient lookup.
|
||||
deleteMap := make(map[string]struct{})
|
||||
for _, client := range clientsToDelete {
|
||||
deleteMap[client.ctx.GetRemoteAddr()] = struct{}{}
|
||||
}
|
||||
|
||||
// Load the current clients associated with the key.
|
||||
func (u *UserMap) deleteClients(key string, clients []*Client) (isDeleteUser bool) {
|
||||
m := datautil.SliceToMapAny(clients, func(c *Client) (string, struct{}) {
|
||||
return c.ctx.GetRemoteAddr(), struct{}{}
|
||||
})
|
||||
allClients, existed := u.m.Load(key)
|
||||
if !existed {
|
||||
// If the key doesn't exist, return false.
|
||||
@@ -116,7 +115,7 @@ func (u *UserMap) deleteClients(key string, clientsToDelete []*Client) (isDelete
|
||||
oldClients := allClients.([]*Client)
|
||||
var remainingClients []*Client
|
||||
for _, client := range oldClients {
|
||||
if _, shouldBeDeleted := deleteMap[client.ctx.GetRemoteAddr()]; !shouldBeDeleted {
|
||||
if _, shouldBeDeleted := m[client.ctx.GetRemoteAddr()]; !shouldBeDeleted {
|
||||
remainingClients = append(remainingClients, client)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,23 +16,26 @@ package msgtransfer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"github.com/openimsdk/tools/db/redisutil"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/OpenIMSDK/tools/mw"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo"
|
||||
kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/mw"
|
||||
"github.com/openimsdk/tools/system/program"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/collectors"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
@@ -50,59 +53,67 @@ type MsgTransfer struct {
|
||||
// and handle the deletion notification message deleted subscriptions topic: msg_to_mongo
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
config *config.GlobalConfig
|
||||
}
|
||||
|
||||
func StartTransfer(config *config.GlobalConfig, prometheusPort int) error {
|
||||
rdb, err := cache.NewRedis(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mongo, err := unrelation.NewMongo(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mongo.CreateMsgIndex(); err != nil {
|
||||
return err
|
||||
}
|
||||
client, err := kdisc.NewDiscoveryRegister(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := client.CreateRpcRootNodes(config.GetServiceNames()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin")))
|
||||
msgModel := cache.NewMsgCacheModel(rdb, config)
|
||||
msgDocModel := unrelation.NewMsgMongoDriver(mongo.GetDatabase(config.Mongo.Database))
|
||||
msgDatabase, err := controller.NewCommonMsgDatabase(msgDocModel, msgModel, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
conversationRpcClient := rpcclient.NewConversationRpcClient(client, config)
|
||||
groupRpcClient := rpcclient.NewGroupRpcClient(client, config)
|
||||
msgTransfer, err := NewMsgTransfer(config, msgDatabase, &conversationRpcClient, &groupRpcClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return msgTransfer.Start(prometheusPort, config)
|
||||
type Config struct {
|
||||
MsgTransfer config.MsgTransfer
|
||||
RedisConfig config.Redis
|
||||
MongodbConfig config.Mongo
|
||||
KafkaConfig config.Kafka
|
||||
ZookeeperConfig config.ZooKeeper
|
||||
Share config.Share
|
||||
WebhooksConfig config.Webhooks
|
||||
}
|
||||
|
||||
func NewMsgTransfer(
|
||||
config *config.GlobalConfig,
|
||||
msgDatabase controller.CommonMsgDatabase,
|
||||
conversationRpcClient *rpcclient.ConversationRpcClient,
|
||||
groupRpcClient *rpcclient.GroupRpcClient,
|
||||
) (*MsgTransfer, error) {
|
||||
historyCH, err := NewOnlineHistoryRedisConsumerHandler(config, msgDatabase, conversationRpcClient, groupRpcClient)
|
||||
func Start(ctx context.Context, index int, config *Config) error {
|
||||
log.CInfo(ctx, "MSG-TRANSFER server is initializing", "prometheusPorts",
|
||||
config.MsgTransfer.Prometheus.Ports, "index", index)
|
||||
mgocli, err := mongoutil.NewMongoDB(ctx, config.MongodbConfig.Build())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rdb, err := redisutil.NewRedisClient(ctx, config.RedisConfig.Build())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client, err := kdisc.NewDiscoveryRegister(&config.ZookeeperConfig, &config.Share)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := client.CreateRpcRootNodes(config.Share.RpcRegisterName.GetServiceNames()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||
grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin")))
|
||||
//todo MsgCacheTimeout
|
||||
msgModel := cache.NewMsgCache(rdb, config.RedisConfig.EnablePipeline)
|
||||
seqModel := cache.NewSeqCache(rdb)
|
||||
msgDocModel, err := mgo.NewMsgMongo(mgocli.GetDB())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
msgDatabase, err := controller.NewCommonMsgDatabase(msgDocModel, msgModel, seqModel, &config.KafkaConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
conversationRpcClient := rpcclient.NewConversationRpcClient(client, config.Share.RpcRegisterName.Conversation)
|
||||
groupRpcClient := rpcclient.NewGroupRpcClient(client, config.Share.RpcRegisterName.Group)
|
||||
msgTransfer, err := NewMsgTransfer(&config.KafkaConfig, msgDatabase, &conversationRpcClient, &groupRpcClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return msgTransfer.Start(index, config)
|
||||
}
|
||||
|
||||
func NewMsgTransfer(kafkaConf *config.Kafka, msgDatabase controller.CommonMsgDatabase,
|
||||
conversationRpcClient *rpcclient.ConversationRpcClient, groupRpcClient *rpcclient.GroupRpcClient) (*MsgTransfer, error) {
|
||||
historyCH, err := NewOnlineHistoryRedisConsumerHandler(kafkaConf, msgDatabase, conversationRpcClient, groupRpcClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
historyMongoCH, err := NewOnlineHistoryMongoConsumerHandler(config, msgDatabase)
|
||||
historyMongoCH, err := NewOnlineHistoryMongoConsumerHandler(kafkaConf, msgDatabase)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -110,14 +121,13 @@ func NewMsgTransfer(
|
||||
return &MsgTransfer{
|
||||
historyCH: historyCH,
|
||||
historyMongoCH: historyMongoCH,
|
||||
config: config,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *MsgTransfer) Start(prometheusPort int, config *config.GlobalConfig) error {
|
||||
fmt.Println("start msg transfer", "prometheusPort:", prometheusPort)
|
||||
if prometheusPort <= 0 {
|
||||
return errs.Wrap(errors.New("prometheusPort not correct"))
|
||||
func (m *MsgTransfer) Start(index int, config *Config) error {
|
||||
prometheusPort, err := datautil.GetElemByIndex(config.MsgTransfer.Prometheus.Ports, index)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.ctx, m.cancel = context.WithCancel(context.Background())
|
||||
|
||||
@@ -129,17 +139,17 @@ func (m *MsgTransfer) Start(prometheusPort int, config *config.GlobalConfig) err
|
||||
go m.historyCH.historyConsumerGroup.RegisterHandleAndConsumer(m.ctx, m.historyCH)
|
||||
go m.historyMongoCH.historyConsumerGroup.RegisterHandleAndConsumer(m.ctx, m.historyMongoCH)
|
||||
|
||||
if config.Prometheus.Enable {
|
||||
if config.MsgTransfer.Prometheus.Enable {
|
||||
go func() {
|
||||
proreg := prometheus.NewRegistry()
|
||||
proreg.MustRegister(
|
||||
collectors.NewGoCollector(),
|
||||
)
|
||||
proreg.MustRegister(prommetrics.GetGrpcCusMetrics("Transfer", config)...)
|
||||
proreg.MustRegister(prommetrics.GetGrpcCusMetrics("Transfer", &config.Share)...)
|
||||
http.Handle("/metrics", promhttp.HandlerFor(proreg, promhttp.HandlerOpts{Registry: proreg}))
|
||||
err := http.ListenAndServe(fmt.Sprintf(":%d", prometheusPort), nil)
|
||||
if err != nil && err != http.ErrServerClosed {
|
||||
netErr = errs.Wrap(err, fmt.Sprintf("prometheus start err: %d", prometheusPort))
|
||||
netErr = errs.WrapMsg(err, "prometheus start error", "prometheusPort", prometheusPort)
|
||||
netDone <- struct{}{}
|
||||
}
|
||||
}()
|
||||
@@ -149,7 +159,7 @@ func (m *MsgTransfer) Start(prometheusPort int, config *config.GlobalConfig) err
|
||||
signal.Notify(sigs, syscall.SIGTERM)
|
||||
select {
|
||||
case <-sigs:
|
||||
util.SIGTERMExit()
|
||||
program.SIGTERMExit()
|
||||
// graceful close kafka client.
|
||||
m.cancel()
|
||||
m.historyCH.historyConsumerGroup.Close()
|
||||
|
||||
@@ -23,18 +23,19 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/IBM/sarama"
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/OpenIMSDK/tools/mcontext"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
"github.com/go-redis/redis"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/kafka"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/mcontext"
|
||||
"github.com/openimsdk/tools/mq/kafka"
|
||||
"github.com/openimsdk/tools/utils/idutil"
|
||||
"github.com/openimsdk/tools/utils/stringutil"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
@@ -80,12 +81,12 @@ type OnlineHistoryRedisConsumerHandler struct {
|
||||
groupRpcClient *rpcclient.GroupRpcClient
|
||||
}
|
||||
|
||||
func NewOnlineHistoryRedisConsumerHandler(
|
||||
config *config.GlobalConfig,
|
||||
database controller.CommonMsgDatabase,
|
||||
conversationRpcClient *rpcclient.ConversationRpcClient,
|
||||
groupRpcClient *rpcclient.GroupRpcClient,
|
||||
) (*OnlineHistoryRedisConsumerHandler, error) {
|
||||
func NewOnlineHistoryRedisConsumerHandler(kafkaConf *config.Kafka, database controller.CommonMsgDatabase,
|
||||
conversationRpcClient *rpcclient.ConversationRpcClient, groupRpcClient *rpcclient.GroupRpcClient) (*OnlineHistoryRedisConsumerHandler, error) {
|
||||
historyConsumerGroup, err := kafka.NewMConsumerGroup(kafkaConf.Build(), kafkaConf.ToRedisGroupID, []string{kafkaConf.ToRedisTopic})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var och OnlineHistoryRedisConsumerHandler
|
||||
och.msgDatabase = database
|
||||
och.msgDistributionCh = make(chan Cmd2Value) // no buffer channel
|
||||
@@ -96,32 +97,7 @@ func NewOnlineHistoryRedisConsumerHandler(
|
||||
}
|
||||
och.conversationRpcClient = conversationRpcClient
|
||||
och.groupRpcClient = groupRpcClient
|
||||
var err error
|
||||
|
||||
var tlsConfig *kafka.TLSConfig
|
||||
if config.Kafka.TLS != nil {
|
||||
tlsConfig = &kafka.TLSConfig{
|
||||
CACrt: config.Kafka.TLS.CACrt,
|
||||
ClientCrt: config.Kafka.TLS.ClientCrt,
|
||||
ClientKey: config.Kafka.TLS.ClientKey,
|
||||
ClientKeyPwd: config.Kafka.TLS.ClientKeyPwd,
|
||||
InsecureSkipVerify: false,
|
||||
}
|
||||
}
|
||||
|
||||
och.historyConsumerGroup, err = kafka.NewMConsumerGroup(&kafka.MConsumerGroupConfig{
|
||||
KafkaVersion: sarama.V2_0_0_0,
|
||||
OffsetsInitial: sarama.OffsetNewest,
|
||||
IsReturnErr: false,
|
||||
UserName: config.Kafka.Username,
|
||||
Password: config.Kafka.Password,
|
||||
}, []string{config.Kafka.LatestMsgToRedis.Topic},
|
||||
config.Kafka.Addr,
|
||||
config.Kafka.ConsumerGroupID.MsgToRedis,
|
||||
tlsConfig,
|
||||
)
|
||||
// statistics.NewStatistics(&och.singleMsgSuccessCount, config.Config.ModuleName.MsgTransferName, fmt.Sprintf("%d
|
||||
// second singleMsgCount insert to mongo", constant.StatisticsTimeInterval), constant.StatisticsTimeInterval)
|
||||
och.historyConsumerGroup = historyConsumerGroup
|
||||
return &och, err
|
||||
}
|
||||
|
||||
@@ -265,22 +241,13 @@ func (och *OnlineHistoryRedisConsumerHandler) handleNotification(
|
||||
}
|
||||
}
|
||||
|
||||
func (och *OnlineHistoryRedisConsumerHandler) toPushTopic(
|
||||
ctx context.Context,
|
||||
key, conversationID string,
|
||||
msgs []*sdkws.MsgData,
|
||||
) {
|
||||
func (och *OnlineHistoryRedisConsumerHandler) toPushTopic(ctx context.Context, key, conversationID string, msgs []*sdkws.MsgData) {
|
||||
for _, v := range msgs {
|
||||
och.msgDatabase.MsgToPushMQ(ctx, key, conversationID, v) // nolint: errcheck
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func (och *OnlineHistoryRedisConsumerHandler) handleMsg(
|
||||
ctx context.Context,
|
||||
key, conversationID string,
|
||||
storageList, notStorageList []*sdkws.MsgData,
|
||||
) {
|
||||
func (och *OnlineHistoryRedisConsumerHandler) handleMsg(ctx context.Context, key, conversationID string, storageList, notStorageList []*sdkws.MsgData) {
|
||||
och.toPushTopic(ctx, key, conversationID, notStorageList)
|
||||
if len(storageList) > 0 {
|
||||
lastSeq, isNewConversation, err := och.msgDatabase.BatchInsertChat2Cache(ctx, conversationID, storageList)
|
||||
@@ -290,7 +257,7 @@ func (och *OnlineHistoryRedisConsumerHandler) handleMsg(
|
||||
}
|
||||
if isNewConversation {
|
||||
switch storageList[0].SessionType {
|
||||
case constant.SuperGroupChatType:
|
||||
case constant.ReadGroupChatType:
|
||||
log.ZInfo(ctx, "group chat first create conversation", "conversationID",
|
||||
conversationID)
|
||||
userIDs, err := och.groupRpcClient.GetGroupMemberIDs(ctx, storageList[0].GroupID)
|
||||
@@ -349,14 +316,8 @@ func (och *OnlineHistoryRedisConsumerHandler) MessagesDistributionHandle() {
|
||||
for i, header := range consumerMessages[i].Headers {
|
||||
arr = append(arr, strconv.Itoa(i), string(header.Key), string(header.Value))
|
||||
}
|
||||
log.ZInfo(
|
||||
ctx,
|
||||
"consumer.kafka.GetContextWithMQHeader",
|
||||
"len",
|
||||
len(consumerMessages[i].Headers),
|
||||
"header",
|
||||
strings.Join(arr, ", "),
|
||||
)
|
||||
log.ZInfo(ctx, "consumer.kafka.GetContextWithMQHeader", "len", len(consumerMessages[i].Headers),
|
||||
"header", strings.Join(arr, ", "))
|
||||
ctxMsg.ctx = kafka.GetContextWithMQHeader(consumerMessages[i].Headers)
|
||||
ctxMsg.message = msgFromMQ
|
||||
log.ZDebug(
|
||||
@@ -381,7 +342,7 @@ func (och *OnlineHistoryRedisConsumerHandler) MessagesDistributionHandle() {
|
||||
log.ZDebug(ctx, "generate map list users len", "length", len(aggregationMsgs))
|
||||
for uniqueKey, v := range aggregationMsgs {
|
||||
if len(v) >= 0 {
|
||||
hashCode := utils.GetHashCode(uniqueKey)
|
||||
hashCode := stringutil.GetHashCode(uniqueKey)
|
||||
channelID := hashCode % ChannelNum
|
||||
newCtx := withAggregationCtx(ctx, v)
|
||||
log.ZDebug(
|
||||
@@ -472,7 +433,7 @@ func (och *OnlineHistoryRedisConsumerHandler) ConsumeClaim(
|
||||
rwLock.Unlock()
|
||||
|
||||
start := time.Now()
|
||||
ctx := mcontext.WithTriggerIDContext(context.Background(), utils.OperationIDGenerator())
|
||||
ctx := mcontext.WithTriggerIDContext(context.Background(), idutil.OperationIDGenerator())
|
||||
log.ZDebug(ctx, "timer trigger msg consumer start", "length", len(buffer))
|
||||
for i := 0; i < len(buffer)/split; i++ {
|
||||
och.msgDistributionCh <- Cmd2Value{Cmd: ConsumerMsgs, Value: TriggerChannelValue{
|
||||
|
||||
@@ -18,42 +18,22 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/IBM/sarama"
|
||||
pbmsg "github.com/OpenIMSDK/protocol/msg"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
|
||||
kfk "github.com/openimsdk/open-im-server/v3/pkg/common/kafka"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
|
||||
pbmsg "github.com/openimsdk/protocol/msg"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/mq/kafka"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
type OnlineHistoryMongoConsumerHandler struct {
|
||||
historyConsumerGroup *kfk.MConsumerGroup
|
||||
historyConsumerGroup *kafka.MConsumerGroup
|
||||
msgDatabase controller.CommonMsgDatabase
|
||||
}
|
||||
|
||||
func NewOnlineHistoryMongoConsumerHandler(config *config.GlobalConfig, database controller.CommonMsgDatabase) (*OnlineHistoryMongoConsumerHandler, error) {
|
||||
var tlsConfig *kfk.TLSConfig
|
||||
if config.Kafka.TLS != nil {
|
||||
tlsConfig = &kfk.TLSConfig{
|
||||
CACrt: config.Kafka.TLS.CACrt,
|
||||
ClientCrt: config.Kafka.TLS.ClientCrt,
|
||||
ClientKey: config.Kafka.TLS.ClientKey,
|
||||
ClientKeyPwd: config.Kafka.TLS.ClientKeyPwd,
|
||||
InsecureSkipVerify: false,
|
||||
}
|
||||
}
|
||||
historyConsumerGroup, err := kfk.NewMConsumerGroup(&kfk.MConsumerGroupConfig{
|
||||
KafkaVersion: sarama.V2_0_0_0,
|
||||
OffsetsInitial: sarama.OffsetNewest,
|
||||
IsReturnErr: false,
|
||||
UserName: config.Kafka.Username,
|
||||
Password: config.Kafka.Password,
|
||||
}, []string{config.Kafka.MsgToMongo.Topic},
|
||||
config.Kafka.Addr,
|
||||
config.Kafka.ConsumerGroupID.MsgToMongo,
|
||||
tlsConfig,
|
||||
)
|
||||
func NewOnlineHistoryMongoConsumerHandler(kafkaConf *config.Kafka, database controller.CommonMsgDatabase) (*OnlineHistoryMongoConsumerHandler, error) {
|
||||
historyConsumerGroup, err := kafka.NewMConsumerGroup(kafkaConf.Build(), kafkaConf.ToMongoGroupID, []string{kafkaConf.ToMongoTopic})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -65,12 +45,7 @@ func NewOnlineHistoryMongoConsumerHandler(config *config.GlobalConfig, database
|
||||
return mc, nil
|
||||
}
|
||||
|
||||
func (mc *OnlineHistoryMongoConsumerHandler) handleChatWs2Mongo(
|
||||
ctx context.Context,
|
||||
cMsg *sarama.ConsumerMessage,
|
||||
key string,
|
||||
session sarama.ConsumerGroupSession,
|
||||
) {
|
||||
func (mc *OnlineHistoryMongoConsumerHandler) handleChatWs2Mongo(ctx context.Context, cMsg *sarama.ConsumerMessage, key string, session sarama.ConsumerGroupSession) {
|
||||
msg := cMsg.Value
|
||||
msgFromMQ := pbmsg.MsgDataToMongoByMQ{}
|
||||
err := proto.Unmarshal(msg, &msgFromMQ)
|
||||
|
||||
+112
-99
@@ -16,122 +16,135 @@ package push
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/webhook"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
"github.com/OpenIMSDK/tools/mcontext"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/http"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
"github.com/openimsdk/tools/mcontext"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
)
|
||||
|
||||
func callbackOfflinePush(
|
||||
ctx context.Context,
|
||||
config *config.GlobalConfig,
|
||||
userIDs []string,
|
||||
msg *sdkws.MsgData,
|
||||
offlinePushUserIDs *[]string,
|
||||
) error {
|
||||
if !config.Callback.CallbackOfflinePush.Enable || msg.ContentType == constant.Typing {
|
||||
return nil
|
||||
}
|
||||
req := &callbackstruct.CallbackBeforePushReq{
|
||||
UserStatusBatchCallbackReq: callbackstruct.UserStatusBatchCallbackReq{
|
||||
UserStatusBaseCallback: callbackstruct.UserStatusBaseCallback{
|
||||
CallbackCommand: callbackstruct.CallbackOfflinePushCommand,
|
||||
OperationID: mcontext.GetOperationID(ctx),
|
||||
PlatformID: int(msg.SenderPlatformID),
|
||||
Platform: constant.PlatformIDToName(int(msg.SenderPlatformID)),
|
||||
func (c *ConsumerHandler) webhookBeforeOfflinePush(ctx context.Context, before *config.BeforeConfig, userIDs []string, msg *sdkws.MsgData, offlinePushUserIDs *[]string) error {
|
||||
return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
|
||||
if msg.ContentType == constant.Typing {
|
||||
return nil
|
||||
}
|
||||
req := &callbackstruct.CallbackBeforePushReq{
|
||||
UserStatusBatchCallbackReq: callbackstruct.UserStatusBatchCallbackReq{
|
||||
UserStatusBaseCallback: callbackstruct.UserStatusBaseCallback{
|
||||
CallbackCommand: callbackstruct.CallbackBeforeOfflinePushCommand,
|
||||
OperationID: mcontext.GetOperationID(ctx),
|
||||
PlatformID: int(msg.SenderPlatformID),
|
||||
Platform: constant.PlatformIDToName(int(msg.SenderPlatformID)),
|
||||
},
|
||||
UserIDList: userIDs,
|
||||
},
|
||||
UserIDList: userIDs,
|
||||
},
|
||||
OfflinePushInfo: msg.OfflinePushInfo,
|
||||
ClientMsgID: msg.ClientMsgID,
|
||||
SendID: msg.SendID,
|
||||
GroupID: msg.GroupID,
|
||||
ContentType: msg.ContentType,
|
||||
SessionType: msg.SessionType,
|
||||
AtUserIDs: msg.AtUserIDList,
|
||||
Content: GetContent(msg),
|
||||
}
|
||||
OfflinePushInfo: msg.OfflinePushInfo,
|
||||
ClientMsgID: msg.ClientMsgID,
|
||||
SendID: msg.SendID,
|
||||
GroupID: msg.GroupID,
|
||||
ContentType: msg.ContentType,
|
||||
SessionType: msg.SessionType,
|
||||
AtUserIDs: msg.AtUserIDList,
|
||||
Content: GetContent(msg),
|
||||
}
|
||||
|
||||
resp := &callbackstruct.CallbackBeforePushResp{}
|
||||
if err := http.CallBackPostReturn(ctx, config.Callback.CallbackUrl, req, resp, config.Callback.CallbackOfflinePush); err != nil {
|
||||
return err
|
||||
}
|
||||
resp := &callbackstruct.CallbackBeforePushResp{}
|
||||
|
||||
if len(resp.UserIDs) != 0 {
|
||||
*offlinePushUserIDs = resp.UserIDs
|
||||
}
|
||||
if resp.OfflinePushInfo != nil {
|
||||
msg.OfflinePushInfo = resp.OfflinePushInfo
|
||||
}
|
||||
return nil
|
||||
if err := c.webhookClient.SyncPost(ctx, req.GetCallbackCommand(), req, resp, before); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(resp.UserIDs) != 0 {
|
||||
*offlinePushUserIDs = resp.UserIDs
|
||||
}
|
||||
if resp.OfflinePushInfo != nil {
|
||||
msg.OfflinePushInfo = resp.OfflinePushInfo
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func callbackOnlinePush(ctx context.Context, config *config.GlobalConfig, userIDs []string, msg *sdkws.MsgData) error {
|
||||
if !config.Callback.CallbackOnlinePush.Enable || utils.Contain(msg.SendID, userIDs...) || msg.ContentType == constant.Typing {
|
||||
return nil
|
||||
}
|
||||
req := callbackstruct.CallbackBeforePushReq{
|
||||
UserStatusBatchCallbackReq: callbackstruct.UserStatusBatchCallbackReq{
|
||||
UserStatusBaseCallback: callbackstruct.UserStatusBaseCallback{
|
||||
CallbackCommand: callbackstruct.CallbackOnlinePushCommand,
|
||||
OperationID: mcontext.GetOperationID(ctx),
|
||||
PlatformID: int(msg.SenderPlatformID),
|
||||
Platform: constant.PlatformIDToName(int(msg.SenderPlatformID)),
|
||||
func (c *ConsumerHandler) webhookBeforeOnlinePush(ctx context.Context, before *config.BeforeConfig, userIDs []string, msg *sdkws.MsgData) error {
|
||||
return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
|
||||
if datautil.Contain(msg.SendID, userIDs...) || msg.ContentType == constant.Typing {
|
||||
return nil
|
||||
}
|
||||
req := callbackstruct.CallbackBeforePushReq{
|
||||
UserStatusBatchCallbackReq: callbackstruct.UserStatusBatchCallbackReq{
|
||||
UserStatusBaseCallback: callbackstruct.UserStatusBaseCallback{
|
||||
CallbackCommand: callbackstruct.CallbackBeforeOnlinePushCommand,
|
||||
OperationID: mcontext.GetOperationID(ctx),
|
||||
PlatformID: int(msg.SenderPlatformID),
|
||||
Platform: constant.PlatformIDToName(int(msg.SenderPlatformID)),
|
||||
},
|
||||
UserIDList: userIDs,
|
||||
},
|
||||
UserIDList: userIDs,
|
||||
},
|
||||
ClientMsgID: msg.ClientMsgID,
|
||||
SendID: msg.SendID,
|
||||
GroupID: msg.GroupID,
|
||||
ContentType: msg.ContentType,
|
||||
SessionType: msg.SessionType,
|
||||
AtUserIDs: msg.AtUserIDList,
|
||||
Content: GetContent(msg),
|
||||
}
|
||||
resp := &callbackstruct.CallbackBeforePushResp{}
|
||||
if err := http.CallBackPostReturn(ctx, config.Callback.CallbackUrl, req, resp, config.Callback.CallbackOnlinePush); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
ClientMsgID: msg.ClientMsgID,
|
||||
SendID: msg.SendID,
|
||||
GroupID: msg.GroupID,
|
||||
ContentType: msg.ContentType,
|
||||
SessionType: msg.SessionType,
|
||||
AtUserIDs: msg.AtUserIDList,
|
||||
Content: GetContent(msg),
|
||||
}
|
||||
resp := &callbackstruct.CallbackBeforePushResp{}
|
||||
if err := c.webhookClient.SyncPost(ctx, req.GetCallbackCommand(), req, resp, before); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func callbackBeforeSuperGroupOnlinePush(
|
||||
func (c *ConsumerHandler) webhookBeforeGroupOnlinePush(
|
||||
ctx context.Context,
|
||||
config *config.GlobalConfig,
|
||||
before *config.BeforeConfig,
|
||||
groupID string,
|
||||
msg *sdkws.MsgData,
|
||||
pushToUserIDs *[]string,
|
||||
) error {
|
||||
if !config.Callback.CallbackBeforeSuperGroupOnlinePush.Enable || msg.ContentType == constant.Typing {
|
||||
return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
|
||||
if msg.ContentType == constant.Typing {
|
||||
return nil
|
||||
}
|
||||
req := callbackstruct.CallbackBeforeSuperGroupOnlinePushReq{
|
||||
UserStatusBaseCallback: callbackstruct.UserStatusBaseCallback{
|
||||
CallbackCommand: callbackstruct.CallbackBeforeGroupOnlinePushCommand,
|
||||
OperationID: mcontext.GetOperationID(ctx),
|
||||
PlatformID: int(msg.SenderPlatformID),
|
||||
Platform: constant.PlatformIDToName(int(msg.SenderPlatformID)),
|
||||
},
|
||||
ClientMsgID: msg.ClientMsgID,
|
||||
SendID: msg.SendID,
|
||||
GroupID: groupID,
|
||||
ContentType: msg.ContentType,
|
||||
SessionType: msg.SessionType,
|
||||
AtUserIDs: msg.AtUserIDList,
|
||||
Content: GetContent(msg),
|
||||
Seq: msg.Seq,
|
||||
}
|
||||
resp := &callbackstruct.CallbackBeforeSuperGroupOnlinePushResp{}
|
||||
if err := c.webhookClient.SyncPost(ctx, req.GetCallbackCommand(), req, resp, before); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(resp.UserIDs) != 0 {
|
||||
*pushToUserIDs = resp.UserIDs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
req := callbackstruct.CallbackBeforeSuperGroupOnlinePushReq{
|
||||
UserStatusBaseCallback: callbackstruct.UserStatusBaseCallback{
|
||||
CallbackCommand: callbackstruct.CallbackSuperGroupOnlinePushCommand,
|
||||
OperationID: mcontext.GetOperationID(ctx),
|
||||
PlatformID: int(msg.SenderPlatformID),
|
||||
Platform: constant.PlatformIDToName(int(msg.SenderPlatformID)),
|
||||
},
|
||||
ClientMsgID: msg.ClientMsgID,
|
||||
SendID: msg.SendID,
|
||||
GroupID: groupID,
|
||||
ContentType: msg.ContentType,
|
||||
SessionType: msg.SessionType,
|
||||
AtUserIDs: msg.AtUserIDList,
|
||||
Content: GetContent(msg),
|
||||
Seq: msg.Seq,
|
||||
}
|
||||
resp := &callbackstruct.CallbackBeforeSuperGroupOnlinePushResp{}
|
||||
if err := http.CallBackPostReturn(ctx, config.Callback.CallbackUrl, req, resp, config.Callback.CallbackBeforeSuperGroupOnlinePush); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(resp.UserIDs) != 0 {
|
||||
*pushToUserIDs = resp.UserIDs
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func GetContent(msg *sdkws.MsgData) string {
|
||||
if msg.ContentType >= constant.NotificationBegin && msg.ContentType <= constant.NotificationEnd {
|
||||
var notification sdkws.NotificationElem
|
||||
if err := json.Unmarshal(msg.Content, ¬ification); err != nil {
|
||||
return ""
|
||||
}
|
||||
return notification.Detail
|
||||
} else {
|
||||
return string(msg.Content)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package push
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
)
|
||||
|
||||
type Consumer struct {
|
||||
pushCh ConsumerHandler
|
||||
// successCount is unused
|
||||
// successCount uint64
|
||||
}
|
||||
|
||||
func NewConsumer(config *config.GlobalConfig, pusher *Pusher) (*Consumer, error) {
|
||||
c, err := NewConsumerHandler(config, pusher)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Consumer{
|
||||
pushCh: *c,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Consumer) Start() {
|
||||
go c.pushCh.pushConsumerGroup.RegisterHandleAndConsumer(context.Background(), &c.pushCh)
|
||||
}
|
||||
@@ -16,8 +16,7 @@ package dummy
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options"
|
||||
)
|
||||
|
||||
func NewClient() *Dummy {
|
||||
@@ -27,6 +26,6 @@ func NewClient() *Dummy {
|
||||
type Dummy struct {
|
||||
}
|
||||
|
||||
func (d *Dummy) Push(ctx context.Context, userIDs []string, title, content string, opts *offlinepush.Opts) error {
|
||||
func (d *Dummy) Push(ctx context.Context, userIDs []string, title, content string, opts *options.Opts) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -16,14 +16,15 @@ package fcm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options"
|
||||
"path/filepath"
|
||||
|
||||
firebase "firebase.google.com/go"
|
||||
"firebase.google.com/go/messaging"
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"google.golang.org/api/option"
|
||||
)
|
||||
@@ -34,29 +35,32 @@ var Terminal = []int{constant.IOSPlatformID, constant.AndroidPlatformID, constan
|
||||
|
||||
type Fcm struct {
|
||||
fcmMsgCli *messaging.Client
|
||||
cache cache.MsgModel
|
||||
cache cache.ThirdCache
|
||||
}
|
||||
|
||||
// NewClient initializes a new FCM client using the Firebase Admin SDK.
|
||||
// It requires the FCM service account credentials file located within the project's configuration directory.
|
||||
func NewClient(globalConfig *config.GlobalConfig, cache cache.MsgModel) *Fcm {
|
||||
projectRoot := config.GetProjectRoot()
|
||||
credentialsFilePath := filepath.Join(projectRoot, "config", globalConfig.Push.Fcm.ServiceAccount)
|
||||
func NewClient(pushConf *config.Push, cache cache.ThirdCache) (*Fcm, error) {
|
||||
projectRoot, err := config.GetProjectRoot()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
credentialsFilePath := filepath.Join(projectRoot, "config", pushConf.FCM.ServiceAccount)
|
||||
opt := option.WithCredentialsFile(credentialsFilePath)
|
||||
fcmApp, err := firebase.NewApp(context.Background(), nil, opt)
|
||||
if err != nil {
|
||||
return nil
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
ctx := context.Background()
|
||||
fcmMsgClient, err := fcmApp.Messaging(ctx)
|
||||
if err != nil {
|
||||
return nil
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
|
||||
return &Fcm{fcmMsgCli: fcmMsgClient, cache: cache}
|
||||
return &Fcm{fcmMsgCli: fcmMsgClient, cache: cache}, nil
|
||||
}
|
||||
|
||||
func (f *Fcm) Push(ctx context.Context, userIDs []string, title, content string, opts *offlinepush.Opts) error {
|
||||
func (f *Fcm) Push(ctx context.Context, userIDs []string, title, content string, opts *options.Opts) error {
|
||||
// accounts->registrationToken
|
||||
allTokens := make(map[string][]string, 0)
|
||||
for _, account := range userIDs {
|
||||
|
||||
@@ -133,13 +133,13 @@ type Payload struct {
|
||||
IsSignal bool `json:"isSignal"`
|
||||
}
|
||||
|
||||
func newPushReq(config *config.GlobalConfig, title, content string) PushReq {
|
||||
func newPushReq(pushConf *config.Push, title, content string) PushReq {
|
||||
pushReq := PushReq{PushMessage: &PushMessage{Notification: &Notification{
|
||||
Title: title,
|
||||
Body: content,
|
||||
ClickType: "startapp",
|
||||
ChannelID: config.Push.GeTui.ChannelID,
|
||||
ChannelName: config.Push.GeTui.ChannelName,
|
||||
ChannelID: pushConf.GeTui.ChannelID,
|
||||
ChannelName: pushConf.GeTui.ChannelName,
|
||||
}}}
|
||||
return pushReq
|
||||
}
|
||||
|
||||
@@ -18,25 +18,24 @@ import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/OpenIMSDK/tools/mcontext"
|
||||
"github.com/OpenIMSDK/tools/utils/splitter"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
http2 "github.com/openimsdk/open-im-server/v3/pkg/common/http"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/mcontext"
|
||||
"github.com/openimsdk/tools/utils/httputil"
|
||||
"github.com/openimsdk/tools/utils/splitter"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrTokenExpire = errors.New("token expire")
|
||||
ErrUserIDEmpty = errors.New("userIDs is empty")
|
||||
ErrTokenExpire = errs.New("token expire")
|
||||
ErrUserIDEmpty = errs.New("userIDs is empty")
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -45,32 +44,34 @@ const (
|
||||
taskURL = "/push/list/message"
|
||||
batchPushURL = "/push/list/alias"
|
||||
|
||||
// codes.
|
||||
// Codes.
|
||||
tokenExpireCode = 10001
|
||||
tokenExpireTime = 60 * 60 * 23
|
||||
taskIDTTL = 1000 * 60 * 60 * 24
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
cache cache.MsgModel
|
||||
cache cache.ThirdCache
|
||||
tokenExpireTime int64
|
||||
taskIDTTL int64
|
||||
config *config.GlobalConfig
|
||||
pushConf *config.Push
|
||||
httpClient *httputil.HTTPClient
|
||||
}
|
||||
|
||||
func NewClient(config *config.GlobalConfig, cache cache.MsgModel) *Client {
|
||||
func NewClient(pushConf *config.Push, cache cache.ThirdCache) *Client {
|
||||
return &Client{cache: cache,
|
||||
tokenExpireTime: tokenExpireTime,
|
||||
taskIDTTL: taskIDTTL,
|
||||
config: config,
|
||||
pushConf: pushConf,
|
||||
httpClient: httputil.NewHTTPClient(httputil.NewClientConfig()),
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Client) Push(ctx context.Context, userIDs []string, title, content string, opts *offlinepush.Opts) error {
|
||||
func (g *Client) Push(ctx context.Context, userIDs []string, title, content string, opts *options.Opts) error {
|
||||
token, err := g.cache.GetGetuiToken(ctx)
|
||||
if err != nil {
|
||||
if errs.Unwrap(err) == redis.Nil {
|
||||
log.ZInfo(ctx, "getui token not exist in redis")
|
||||
log.ZDebug(ctx, "getui token not exist in redis")
|
||||
token, err = g.getTokenAndSave2Redis(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -79,7 +80,7 @@ func (g *Client) Push(ctx context.Context, userIDs []string, title, content stri
|
||||
return err
|
||||
}
|
||||
}
|
||||
pushReq := newPushReq(g.config, title, content)
|
||||
pushReq := newPushReq(g.pushConf, title, content)
|
||||
pushReq.setPushChannel(title, content)
|
||||
if len(userIDs) > 1 {
|
||||
maxNum := 999
|
||||
@@ -114,13 +115,13 @@ func (g *Client) Push(ctx context.Context, userIDs []string, title, content stri
|
||||
func (g *Client) Auth(ctx context.Context, timeStamp int64) (token string, expireTime int64, err error) {
|
||||
h := sha256.New()
|
||||
h.Write(
|
||||
[]byte(g.config.Push.GeTui.AppKey + strconv.Itoa(int(timeStamp)) + g.config.Push.GeTui.MasterSecret),
|
||||
[]byte(g.pushConf.GeTui.AppKey + strconv.Itoa(int(timeStamp)) + g.pushConf.GeTui.MasterSecret),
|
||||
)
|
||||
sign := hex.EncodeToString(h.Sum(nil))
|
||||
reqAuth := AuthReq{
|
||||
Sign: sign,
|
||||
Timestamp: strconv.Itoa(int(timeStamp)),
|
||||
AppKey: g.config.Push.GeTui.AppKey,
|
||||
AppKey: g.pushConf.GeTui.AppKey,
|
||||
}
|
||||
respAuth := AuthResp{}
|
||||
err = g.request(ctx, authURL, reqAuth, "", &respAuth)
|
||||
@@ -163,7 +164,7 @@ func (g *Client) request(ctx context.Context, url string, input any, token strin
|
||||
header := map[string]string{"token": token}
|
||||
resp := &Resp{}
|
||||
resp.Data = output
|
||||
return g.postReturn(ctx, g.config.Push.GeTui.PushUrl+url, header, input, resp, 3)
|
||||
return g.postReturn(ctx, g.pushConf.GeTui.PushUrl+url, header, input, resp, 3)
|
||||
}
|
||||
|
||||
func (g *Client) postReturn(
|
||||
@@ -174,7 +175,7 @@ func (g *Client) postReturn(
|
||||
output RespI,
|
||||
timeout int,
|
||||
) error {
|
||||
err := http2.PostReturn(ctx, url, header, input, output, timeout)
|
||||
err := g.httpClient.PostReturn(ctx, url, header, input, output, timeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -32,8 +32,8 @@ func (a *Audience) set(key string, v []string) {
|
||||
a.audience = make(map[string][]string)
|
||||
a.Object = a.audience
|
||||
}
|
||||
//v, ok = this.audience[key]
|
||||
//if ok {
|
||||
// v, ok = this.audience[key]
|
||||
// if ok {
|
||||
// return
|
||||
//}
|
||||
a.audience[key] = v
|
||||
|
||||
@@ -56,8 +56,8 @@ func (n *Notification) SetExtras(extras Extras) {
|
||||
n.Android.Extras = extras
|
||||
}
|
||||
|
||||
func (n *Notification) SetAndroidIntent(config *config.GlobalConfig) {
|
||||
n.Android.Intent.URL = config.Push.Jpns.PushIntent
|
||||
func (n *Notification) SetAndroidIntent(pushConf *config.Push) {
|
||||
n.Android.Intent.URL = pushConf.JPNS.PushIntent
|
||||
}
|
||||
|
||||
func (n *Notification) IOSEnableMutableContent() {
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
package body
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -39,7 +39,7 @@ func (p *Platform) Set(os string) error {
|
||||
} else {
|
||||
switch p.Os.(type) {
|
||||
case string:
|
||||
return errors.New("platform is all")
|
||||
return errs.New("platform is all")
|
||||
default:
|
||||
}
|
||||
}
|
||||
@@ -61,7 +61,7 @@ func (p *Platform) Set(os string) error {
|
||||
p.osArry = append(p.osArry, os)
|
||||
p.Os = p.osArry
|
||||
default:
|
||||
return errors.New("unknow platform")
|
||||
return errs.New("unknow platform")
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -74,7 +74,7 @@ func (p *Platform) SetPlatform(platform string) error {
|
||||
case constant.IOSPlatformStr:
|
||||
return p.SetIOS()
|
||||
default:
|
||||
return errors.New("platform err")
|
||||
return errs.New("platform err")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,19 +18,22 @@ import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/jpush/body"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
http2 "github.com/openimsdk/open-im-server/v3/pkg/common/http"
|
||||
"github.com/openimsdk/tools/utils/httputil"
|
||||
)
|
||||
|
||||
type JPush struct {
|
||||
config *config.GlobalConfig
|
||||
pushConf *config.Push
|
||||
httpClient *httputil.HTTPClient
|
||||
}
|
||||
|
||||
func NewClient(config *config.GlobalConfig) *JPush {
|
||||
return &JPush{config: config}
|
||||
func NewClient(pushConf *config.Push) *JPush {
|
||||
return &JPush{pushConf: pushConf,
|
||||
httpClient: httputil.NewHTTPClient(httputil.NewClientConfig()),
|
||||
}
|
||||
}
|
||||
|
||||
func (j *JPush) Auth(apiKey, secretKey string, timeStamp int64) (token string, err error) {
|
||||
@@ -48,7 +51,7 @@ func (j *JPush) getAuthorization(appKey string, masterSecret string) string {
|
||||
return Authorization
|
||||
}
|
||||
|
||||
func (j *JPush) Push(ctx context.Context, userIDs []string, title, content string, opts *offlinepush.Opts) error {
|
||||
func (j *JPush) Push(ctx context.Context, userIDs []string, title, content string, opts *options.Opts) error {
|
||||
var pf body.Platform
|
||||
pf.SetAll()
|
||||
var au body.Audience
|
||||
@@ -61,12 +64,12 @@ func (j *JPush) Push(ctx context.Context, userIDs []string, title, content strin
|
||||
no.IOSEnableMutableContent()
|
||||
no.SetExtras(extras)
|
||||
no.SetAlert(title)
|
||||
no.SetAndroidIntent(j.config)
|
||||
no.SetAndroidIntent(j.pushConf)
|
||||
|
||||
var msg body.Message
|
||||
msg.SetMsgContent(content)
|
||||
var opt body.Options
|
||||
opt.SetApnsProduction(j.config.IOSPush.Production)
|
||||
opt.SetApnsProduction(j.pushConf.IOSPush.Production)
|
||||
var pushObj body.PushObj
|
||||
pushObj.SetPlatform(&pf)
|
||||
pushObj.SetAudience(&au)
|
||||
@@ -78,11 +81,11 @@ func (j *JPush) Push(ctx context.Context, userIDs []string, title, content strin
|
||||
}
|
||||
|
||||
func (j *JPush) request(ctx context.Context, po body.PushObj, resp any, timeout int) error {
|
||||
return http2.PostReturn(
|
||||
return j.httpClient.PostReturn(
|
||||
ctx,
|
||||
j.config.Push.Jpns.PushUrl,
|
||||
j.pushConf.JPNS.PushURL,
|
||||
map[string]string{
|
||||
"Authorization": j.getAuthorization(j.config.Push.Jpns.AppKey, j.config.Push.Jpns.MasterSecret),
|
||||
"Authorization": j.getAuthorization(j.pushConf.JPNS.AppKey, j.pushConf.JPNS.MasterSecret),
|
||||
},
|
||||
po,
|
||||
resp,
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package offlinepush
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// OfflinePusher Offline Pusher.
|
||||
type OfflinePusher interface {
|
||||
Push(ctx context.Context, userIDs []string, title, content string, opts *Opts) error
|
||||
}
|
||||
|
||||
// Opts opts.
|
||||
type Opts struct {
|
||||
Signal *Signal
|
||||
IOSPushSound string
|
||||
IOSBadgeCount bool
|
||||
Ex string
|
||||
}
|
||||
|
||||
// Signal message id.
|
||||
type Signal struct {
|
||||
ClientMsgID string
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package offlinepush
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/dummy"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/fcm"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/getui"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/jpush"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
)
|
||||
|
||||
const (
|
||||
geTUI = "getui"
|
||||
firebase = "fcm"
|
||||
jPush = "jpush"
|
||||
)
|
||||
|
||||
// OfflinePusher Offline Pusher.
|
||||
type OfflinePusher interface {
|
||||
Push(ctx context.Context, userIDs []string, title, content string, opts *options.Opts) error
|
||||
}
|
||||
|
||||
func NewOfflinePusher(pushConf *config.Push, cache cache.ThirdCache) (OfflinePusher, error) {
|
||||
var offlinePusher OfflinePusher
|
||||
switch pushConf.Enable {
|
||||
case geTUI:
|
||||
offlinePusher = getui.NewClient(pushConf, cache)
|
||||
case firebase:
|
||||
return fcm.NewClient(pushConf, cache)
|
||||
case jPush:
|
||||
offlinePusher = jpush.NewClient(pushConf)
|
||||
default:
|
||||
offlinePusher = dummy.NewClient()
|
||||
}
|
||||
return offlinePusher, nil
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package options
|
||||
|
||||
// Opts opts.
|
||||
type Opts struct {
|
||||
Signal *Signal
|
||||
IOSPushSound string
|
||||
IOSBadgeCount bool
|
||||
Ex string
|
||||
}
|
||||
|
||||
// Signal message id.
|
||||
type Signal struct {
|
||||
ClientMsgID string
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
package push
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/openimsdk/protocol/msggateway"
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
"github.com/openimsdk/tools/discovery"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"google.golang.org/grpc"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
KUBERNETES = "k8s"
|
||||
ZOOKEEPER = "zookeeper"
|
||||
)
|
||||
|
||||
type OnlinePusher interface {
|
||||
GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData,
|
||||
pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error)
|
||||
GetOnlinePushFailedUserIDs(ctx context.Context, msg *sdkws.MsgData, wsResults []*msggateway.SingleMsgToUserResults,
|
||||
pushToUserIDs *[]string) []string
|
||||
}
|
||||
|
||||
type emptyOnlinePUsher struct{}
|
||||
|
||||
func newEmptyOnlinePUsher() *emptyOnlinePUsher {
|
||||
return &emptyOnlinePUsher{}
|
||||
}
|
||||
|
||||
func (emptyOnlinePUsher) GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData,
|
||||
pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) {
|
||||
log.ZWarn(ctx, "emptyOnlinePUsher GetConnsAndOnlinePush", nil)
|
||||
return nil, nil
|
||||
}
|
||||
func (u emptyOnlinePUsher) GetOnlinePushFailedUserIDs(ctx context.Context, msg *sdkws.MsgData,
|
||||
wsResults []*msggateway.SingleMsgToUserResults, pushToUserIDs *[]string) []string {
|
||||
log.ZWarn(ctx, "emptyOnlinePUsher GetOnlinePushFailedUserIDs", nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewOnlinePusher(disCov discovery.SvcDiscoveryRegistry, config *Config) OnlinePusher {
|
||||
switch config.Share.Env {
|
||||
case KUBERNETES:
|
||||
return NewK8sStaticConsistentHash(disCov, config)
|
||||
case ZOOKEEPER:
|
||||
return NewDefaultAllNode(disCov, config)
|
||||
default:
|
||||
return newEmptyOnlinePUsher()
|
||||
}
|
||||
}
|
||||
|
||||
type DefaultAllNode struct {
|
||||
disCov discovery.SvcDiscoveryRegistry
|
||||
config *Config
|
||||
}
|
||||
|
||||
func NewDefaultAllNode(disCov discovery.SvcDiscoveryRegistry, config *Config) *DefaultAllNode {
|
||||
return &DefaultAllNode{disCov: disCov, config: config}
|
||||
}
|
||||
|
||||
func (d *DefaultAllNode) GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData,
|
||||
pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) {
|
||||
conns, err := d.disCov.GetConns(ctx, d.config.Share.RpcRegisterName.MessageGateway)
|
||||
log.ZDebug(ctx, "get gateway conn", "conn length", len(conns))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
mu sync.Mutex
|
||||
wg = errgroup.Group{}
|
||||
input = &msggateway.OnlineBatchPushOneMsgReq{MsgData: msg, PushToUserIDs: pushToUserIDs}
|
||||
maxWorkers = d.config.RpcConfig.MaxConcurrentWorkers
|
||||
)
|
||||
|
||||
if maxWorkers < 3 {
|
||||
maxWorkers = 3
|
||||
}
|
||||
|
||||
wg.SetLimit(maxWorkers)
|
||||
|
||||
// Online push message
|
||||
for _, conn := range conns {
|
||||
conn := conn // loop var safe
|
||||
wg.Go(func() error {
|
||||
msgClient := msggateway.NewMsgGatewayClient(conn)
|
||||
reply, err := msgClient.SuperGroupOnlineBatchPushOneMsg(ctx, input)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.ZDebug(ctx, "push result", "reply", reply)
|
||||
if reply != nil && reply.SinglePushResult != nil {
|
||||
mu.Lock()
|
||||
wsResults = append(wsResults, reply.SinglePushResult...)
|
||||
mu.Unlock()
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
_ = wg.Wait()
|
||||
|
||||
// always return nil
|
||||
return wsResults, nil
|
||||
}
|
||||
|
||||
func (d *DefaultAllNode) GetOnlinePushFailedUserIDs(_ context.Context, msg *sdkws.MsgData,
|
||||
wsResults []*msggateway.SingleMsgToUserResults, pushToUserIDs *[]string) []string {
|
||||
|
||||
onlineSuccessUserIDs := []string{msg.SendID}
|
||||
for _, v := range wsResults {
|
||||
//message sender do not need offline push
|
||||
if msg.SendID == v.UserID {
|
||||
continue
|
||||
}
|
||||
// mobile online push success
|
||||
if v.OnlinePush {
|
||||
onlineSuccessUserIDs = append(onlineSuccessUserIDs, v.UserID)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return datautil.SliceSub(*pushToUserIDs, onlineSuccessUserIDs)
|
||||
}
|
||||
|
||||
type K8sStaticConsistentHash struct {
|
||||
disCov discovery.SvcDiscoveryRegistry
|
||||
config *Config
|
||||
}
|
||||
|
||||
func NewK8sStaticConsistentHash(disCov discovery.SvcDiscoveryRegistry, config *Config) *K8sStaticConsistentHash {
|
||||
return &K8sStaticConsistentHash{disCov: disCov, config: config}
|
||||
}
|
||||
|
||||
func (k *K8sStaticConsistentHash) GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData,
|
||||
pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) {
|
||||
|
||||
var usersHost = make(map[string][]string)
|
||||
for _, v := range pushToUserIDs {
|
||||
tHost, err := k.disCov.GetUserIdHashGatewayHost(ctx, v)
|
||||
if err != nil {
|
||||
log.ZError(ctx, "get msg gateway hash error", err)
|
||||
return nil, err
|
||||
}
|
||||
tUsers, tbl := usersHost[tHost]
|
||||
if tbl {
|
||||
tUsers = append(tUsers, v)
|
||||
usersHost[tHost] = tUsers
|
||||
} else {
|
||||
usersHost[tHost] = []string{v}
|
||||
}
|
||||
}
|
||||
log.ZDebug(ctx, "genUsers send hosts struct:", "usersHost", usersHost)
|
||||
var usersConns = make(map[*grpc.ClientConn][]string)
|
||||
for host, userIds := range usersHost {
|
||||
tconn, _ := k.disCov.GetConn(ctx, host)
|
||||
usersConns[tconn] = userIds
|
||||
}
|
||||
var (
|
||||
mu sync.Mutex
|
||||
wg = errgroup.Group{}
|
||||
maxWorkers = k.config.RpcConfig.MaxConcurrentWorkers
|
||||
)
|
||||
if maxWorkers < 3 {
|
||||
maxWorkers = 3
|
||||
}
|
||||
wg.SetLimit(maxWorkers)
|
||||
for conn, userIds := range usersConns {
|
||||
tcon := conn
|
||||
tuserIds := userIds
|
||||
wg.Go(func() error {
|
||||
input := &msggateway.OnlineBatchPushOneMsgReq{MsgData: msg, PushToUserIDs: tuserIds}
|
||||
msgClient := msggateway.NewMsgGatewayClient(tcon)
|
||||
reply, err := msgClient.SuperGroupOnlineBatchPushOneMsg(ctx, input)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
log.ZDebug(ctx, "push result", "reply", reply)
|
||||
if reply != nil && reply.SinglePushResult != nil {
|
||||
mu.Lock()
|
||||
wsResults = append(wsResults, reply.SinglePushResult...)
|
||||
mu.Unlock()
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
_ = wg.Wait()
|
||||
return wsResults, nil
|
||||
}
|
||||
func (k *K8sStaticConsistentHash) GetOnlinePushFailedUserIDs(_ context.Context, _ *sdkws.MsgData,
|
||||
wsResults []*msggateway.SingleMsgToUserResults, _ *[]string) []string {
|
||||
var needOfflinePushUserIDs []string
|
||||
for _, v := range wsResults {
|
||||
if !v.OnlinePush {
|
||||
needOfflinePushUserIDs = append(needOfflinePushUserIDs, v.UserID)
|
||||
}
|
||||
}
|
||||
return needOfflinePushUserIDs
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package push
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
|
||||
pbpush "github.com/openimsdk/protocol/push"
|
||||
"github.com/openimsdk/tools/db/redisutil"
|
||||
"github.com/openimsdk/tools/discovery"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type pushServer struct {
|
||||
database controller.PushDatabase
|
||||
disCov discovery.SvcDiscoveryRegistry
|
||||
offlinePusher offlinepush.OfflinePusher
|
||||
pushCh *ConsumerHandler
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
RpcConfig config.Push
|
||||
RedisConfig config.Redis
|
||||
MongodbConfig config.Mongo
|
||||
KafkaConfig config.Kafka
|
||||
ZookeeperConfig config.ZooKeeper
|
||||
NotificationConfig config.Notification
|
||||
Share config.Share
|
||||
WebhooksConfig config.Webhooks
|
||||
LocalCacheConfig config.LocalCache
|
||||
}
|
||||
|
||||
func (p pushServer) PushMsg(ctx context.Context, req *pbpush.PushMsgReq) (*pbpush.PushMsgResp, error) {
|
||||
//todo reserved Interface
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (p pushServer) DelUserPushToken(ctx context.Context,
|
||||
req *pbpush.DelUserPushTokenReq) (resp *pbpush.DelUserPushTokenResp, err error) {
|
||||
if err = p.database.DelFcmToken(ctx, req.UserID, int(req.PlatformID)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pbpush.DelUserPushTokenResp{}, nil
|
||||
}
|
||||
|
||||
func Start(ctx context.Context, config *Config, client discovery.SvcDiscoveryRegistry, server *grpc.Server) error {
|
||||
rdb, err := redisutil.NewRedisClient(ctx, config.RedisConfig.Build())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cacheModel := cache.NewThirdCache(rdb)
|
||||
offlinePusher, err := offlinepush.NewOfflinePusher(&config.RpcConfig, cacheModel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
database := controller.NewPushDatabase(cacheModel)
|
||||
|
||||
consumer, err := NewConsumerHandler(config, offlinePusher, rdb, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pbpush.RegisterPushMsgServiceServer(server, &pushServer{
|
||||
database: database,
|
||||
disCov: client,
|
||||
offlinePusher: offlinePusher,
|
||||
pushCh: consumer,
|
||||
})
|
||||
go consumer.pushConsumerGroup.RegisterHandleAndConsumer(ctx, consumer)
|
||||
return nil
|
||||
}
|
||||
+297
-45
@@ -16,49 +16,64 @@ package push
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/webhook"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpccache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/util/conversationutil"
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
"github.com/openimsdk/tools/discovery"
|
||||
"github.com/openimsdk/tools/mcontext"
|
||||
"github.com/openimsdk/tools/utils/jsonutil"
|
||||
"github.com/redis/go-redis/v9"
|
||||
|
||||
"github.com/IBM/sarama"
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
pbchat "github.com/OpenIMSDK/protocol/msg"
|
||||
pbpush "github.com/OpenIMSDK/protocol/push"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
kfk "github.com/openimsdk/open-im-server/v3/pkg/common/kafka"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
pbchat "github.com/openimsdk/protocol/msg"
|
||||
pbpush "github.com/openimsdk/protocol/push"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/mq/kafka"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
"github.com/openimsdk/tools/utils/timeutil"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
type ConsumerHandler struct {
|
||||
pushConsumerGroup *kfk.MConsumerGroup
|
||||
pusher *Pusher
|
||||
pushConsumerGroup *kafka.MConsumerGroup
|
||||
offlinePusher offlinepush.OfflinePusher
|
||||
onlinePusher OnlinePusher
|
||||
groupLocalCache *rpccache.GroupLocalCache
|
||||
conversationLocalCache *rpccache.ConversationLocalCache
|
||||
msgRpcClient rpcclient.MessageRpcClient
|
||||
conversationRpcClient rpcclient.ConversationRpcClient
|
||||
groupRpcClient rpcclient.GroupRpcClient
|
||||
webhookClient *webhook.Client
|
||||
config *Config
|
||||
}
|
||||
|
||||
func NewConsumerHandler(config *config.GlobalConfig, pusher *Pusher) (*ConsumerHandler, error) {
|
||||
func NewConsumerHandler(config *Config, offlinePusher offlinepush.OfflinePusher, rdb redis.UniversalClient,
|
||||
client discovery.SvcDiscoveryRegistry) (*ConsumerHandler, error) {
|
||||
var consumerHandler ConsumerHandler
|
||||
consumerHandler.pusher = pusher
|
||||
var err error
|
||||
var tlsConfig *kfk.TLSConfig
|
||||
if config.Kafka.TLS != nil {
|
||||
tlsConfig = &kfk.TLSConfig{
|
||||
CACrt: config.Kafka.TLS.CACrt,
|
||||
ClientCrt: config.Kafka.TLS.ClientCrt,
|
||||
ClientKey: config.Kafka.TLS.ClientKey,
|
||||
ClientKeyPwd: config.Kafka.TLS.ClientKeyPwd,
|
||||
InsecureSkipVerify: false,
|
||||
}
|
||||
}
|
||||
consumerHandler.pushConsumerGroup, err = kfk.NewMConsumerGroup(&kfk.MConsumerGroupConfig{
|
||||
KafkaVersion: sarama.V2_0_0_0,
|
||||
OffsetsInitial: sarama.OffsetNewest,
|
||||
IsReturnErr: false,
|
||||
UserName: config.Kafka.Username,
|
||||
Password: config.Kafka.Password,
|
||||
}, []string{config.Kafka.MsgToPush.Topic}, config.Kafka.Addr,
|
||||
config.Kafka.ConsumerGroupID.MsgToPush,
|
||||
tlsConfig)
|
||||
consumerHandler.pushConsumerGroup, err = kafka.NewMConsumerGroup(config.KafkaConfig.Build(), config.KafkaConfig.ToPushGroupID,
|
||||
[]string{config.KafkaConfig.ToPushTopic})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
consumerHandler.offlinePusher = offlinePusher
|
||||
consumerHandler.onlinePusher = NewOnlinePusher(client, config)
|
||||
consumerHandler.groupRpcClient = rpcclient.NewGroupRpcClient(client, config.Share.RpcRegisterName.Group)
|
||||
consumerHandler.groupLocalCache = rpccache.NewGroupLocalCache(consumerHandler.groupRpcClient, &config.LocalCacheConfig, rdb)
|
||||
consumerHandler.msgRpcClient = rpcclient.NewMessageRpcClient(client, config.Share.RpcRegisterName.Msg)
|
||||
consumerHandler.conversationRpcClient = rpcclient.NewConversationRpcClient(client, config.Share.RpcRegisterName.Conversation)
|
||||
consumerHandler.conversationLocalCache = rpccache.NewConversationLocalCache(consumerHandler.conversationRpcClient,
|
||||
&config.LocalCacheConfig, rdb)
|
||||
consumerHandler.webhookClient = webhook.NewWebhookClient(config.WebhooksConfig.URL)
|
||||
consumerHandler.config = config
|
||||
return &consumerHandler, nil
|
||||
}
|
||||
|
||||
@@ -73,38 +88,35 @@ func (c *ConsumerHandler) handleMs2PsChat(ctx context.Context, msg []byte) {
|
||||
ConversationID: msgFromMQ.ConversationID,
|
||||
}
|
||||
sec := msgFromMQ.MsgData.SendTime / 1000
|
||||
nowSec := utils.GetCurrentTimestampBySecond()
|
||||
nowSec := timeutil.GetCurrentTimestampBySecond()
|
||||
log.ZDebug(ctx, "push msg", "msg", pbData.String(), "sec", sec, "nowSec", nowSec)
|
||||
if nowSec-sec > 10 {
|
||||
return
|
||||
}
|
||||
var err error
|
||||
switch msgFromMQ.MsgData.SessionType {
|
||||
case constant.SuperGroupChatType:
|
||||
err = c.pusher.Push2SuperGroup(ctx, pbData.MsgData.GroupID, pbData.MsgData)
|
||||
case constant.ReadGroupChatType:
|
||||
err = c.Push2SuperGroup(ctx, pbData.MsgData.GroupID, pbData.MsgData)
|
||||
default:
|
||||
var pushUserIDList []string
|
||||
isSenderSync := utils.GetSwitchFromOptions(pbData.MsgData.Options, constant.IsSenderSync)
|
||||
isSenderSync := datautil.GetSwitchFromOptions(pbData.MsgData.Options, constant.IsSenderSync)
|
||||
if !isSenderSync || pbData.MsgData.SendID == pbData.MsgData.RecvID {
|
||||
pushUserIDList = append(pushUserIDList, pbData.MsgData.RecvID)
|
||||
} else {
|
||||
pushUserIDList = append(pushUserIDList, pbData.MsgData.RecvID, pbData.MsgData.SendID)
|
||||
}
|
||||
err = c.pusher.Push2User(ctx, pushUserIDList, pbData.MsgData)
|
||||
err = c.Push2User(ctx, pushUserIDList, pbData.MsgData)
|
||||
}
|
||||
if err != nil {
|
||||
if err == errNoOfflinePusher {
|
||||
log.ZWarn(ctx, "offline push failed", err, "msg", pbData.String())
|
||||
} else {
|
||||
log.ZError(ctx, "push failed", err, "msg", pbData.String())
|
||||
}
|
||||
log.ZError(ctx, "push failed", err, "msg", pbData.String())
|
||||
}
|
||||
}
|
||||
func (ConsumerHandler) Setup(_ sarama.ConsumerGroupSession) error { return nil }
|
||||
func (ConsumerHandler) Cleanup(_ sarama.ConsumerGroupSession) error { return nil }
|
||||
func (c *ConsumerHandler) ConsumeClaim(sess sarama.ConsumerGroupSession,
|
||||
claim sarama.ConsumerGroupClaim,
|
||||
) error {
|
||||
|
||||
func (*ConsumerHandler) Setup(sarama.ConsumerGroupSession) error { return nil }
|
||||
|
||||
func (*ConsumerHandler) Cleanup(sarama.ConsumerGroupSession) error { return nil }
|
||||
|
||||
func (c *ConsumerHandler) ConsumeClaim(sess sarama.ConsumerGroupSession, claim sarama.ConsumerGroupClaim) error {
|
||||
for msg := range claim.Messages() {
|
||||
ctx := c.pushConsumerGroup.GetContextFromMsg(msg)
|
||||
c.handleMs2PsChat(ctx, msg.Value)
|
||||
@@ -112,3 +124,243 @@ func (c *ConsumerHandler) ConsumeClaim(sess sarama.ConsumerGroupSession,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Push2User Suitable for two types of conversations, one is SingleChatType and the other is NotificationChatType.
|
||||
func (c *ConsumerHandler) Push2User(ctx context.Context, userIDs []string, msg *sdkws.MsgData) error {
|
||||
log.ZDebug(ctx, "Get msg from msg_transfer And push msg", "userIDs", userIDs, "msg", msg.String())
|
||||
if err := c.webhookBeforeOnlinePush(ctx, &c.config.WebhooksConfig.BeforeOnlinePush, userIDs, msg); err != nil {
|
||||
return err
|
||||
}
|
||||
wsResults, err := c.onlinePusher.GetConnsAndOnlinePush(ctx, msg, userIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.ZDebug(ctx, "single and notification push result", "result", wsResults, "msg", msg, "push_to_userID", userIDs)
|
||||
|
||||
if !c.shouldPushOffline(ctx, msg) {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, v := range wsResults {
|
||||
//message sender do not need offline push
|
||||
if msg.SendID == v.UserID {
|
||||
continue
|
||||
}
|
||||
//receiver online push success
|
||||
if v.OnlinePush {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
offlinePUshUserID := []string{msg.RecvID}
|
||||
|
||||
//receiver offline push
|
||||
if err = c.webhookBeforeOfflinePush(ctx, &c.config.WebhooksConfig.BeforeOfflinePush,
|
||||
offlinePUshUserID, msg, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = c.offlinePushMsg(ctx, msg, offlinePUshUserID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ConsumerHandler) shouldPushOffline(_ context.Context, msg *sdkws.MsgData) bool {
|
||||
isOfflinePush := datautil.GetSwitchFromOptions(msg.Options, constant.IsOfflinePush)
|
||||
if !isOfflinePush {
|
||||
return false
|
||||
}
|
||||
if msg.ContentType == constant.SignalingNotification {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *ConsumerHandler) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws.MsgData) (err error) {
|
||||
log.ZDebug(ctx, "Get super group msg from msg_transfer and push msg", "msg", msg.String(), "groupID", groupID)
|
||||
var pushToUserIDs []string
|
||||
if err = c.webhookBeforeGroupOnlinePush(ctx, &c.config.WebhooksConfig.BeforeGroupOnlinePush, groupID, msg,
|
||||
&pushToUserIDs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = c.groupMessagesHandler(ctx, groupID, &pushToUserIDs, msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wsResults, err := c.onlinePusher.GetConnsAndOnlinePush(ctx, msg, pushToUserIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.ZDebug(ctx, "group push result", "result", wsResults, "msg", msg)
|
||||
|
||||
if !c.shouldPushOffline(ctx, msg) {
|
||||
return nil
|
||||
}
|
||||
needOfflinePushUserIDs := c.onlinePusher.GetOnlinePushFailedUserIDs(ctx, msg, wsResults, &pushToUserIDs)
|
||||
|
||||
//filter some user, like don not disturb or don't need offline push etc.
|
||||
needOfflinePushUserIDs, err = c.filterGroupMessageOfflinePush(ctx, groupID, msg, needOfflinePushUserIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Use offline push messaging
|
||||
if len(needOfflinePushUserIDs) > 0 {
|
||||
var offlinePushUserIDs []string
|
||||
err = c.webhookBeforeOfflinePush(ctx, &c.config.WebhooksConfig.BeforeOfflinePush, needOfflinePushUserIDs, msg, &offlinePushUserIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(offlinePushUserIDs) > 0 {
|
||||
needOfflinePushUserIDs = offlinePushUserIDs
|
||||
}
|
||||
|
||||
err = c.offlinePushMsg(ctx, msg, needOfflinePushUserIDs)
|
||||
if err != nil {
|
||||
log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg)
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
func (c *ConsumerHandler) groupMessagesHandler(ctx context.Context, groupID string, pushToUserIDs *[]string, msg *sdkws.MsgData) (err error) {
|
||||
if len(*pushToUserIDs) == 0 {
|
||||
*pushToUserIDs, err = c.groupLocalCache.GetGroupMemberIDs(ctx, groupID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch msg.ContentType {
|
||||
case constant.MemberQuitNotification:
|
||||
var tips sdkws.MemberQuitTips
|
||||
if unmarshalNotificationElem(msg.Content, &tips) != nil {
|
||||
return err
|
||||
}
|
||||
if err = c.DeleteMemberAndSetConversationSeq(ctx, groupID, []string{tips.QuitUser.UserID}); err != nil {
|
||||
log.ZError(ctx, "MemberQuitNotification DeleteMemberAndSetConversationSeq", err, "groupID", groupID, "userID", tips.QuitUser.UserID)
|
||||
}
|
||||
*pushToUserIDs = append(*pushToUserIDs, tips.QuitUser.UserID)
|
||||
case constant.MemberKickedNotification:
|
||||
var tips sdkws.MemberKickedTips
|
||||
if unmarshalNotificationElem(msg.Content, &tips) != nil {
|
||||
return err
|
||||
}
|
||||
kickedUsers := datautil.Slice(tips.KickedUserList, func(e *sdkws.GroupMemberFullInfo) string { return e.UserID })
|
||||
if err = c.DeleteMemberAndSetConversationSeq(ctx, groupID, kickedUsers); err != nil {
|
||||
log.ZError(ctx, "MemberKickedNotification DeleteMemberAndSetConversationSeq", err, "groupID", groupID, "userIDs", kickedUsers)
|
||||
}
|
||||
|
||||
*pushToUserIDs = append(*pushToUserIDs, kickedUsers...)
|
||||
case constant.GroupDismissedNotification:
|
||||
if msgprocessor.IsNotification(msgprocessor.GetConversationIDByMsg(msg)) { // 消息先到,通知后到
|
||||
var tips sdkws.GroupDismissedTips
|
||||
if unmarshalNotificationElem(msg.Content, &tips) != nil {
|
||||
return err
|
||||
}
|
||||
log.ZInfo(ctx, "GroupDismissedNotificationInfo****", "groupID", groupID, "num", len(*pushToUserIDs), "list", pushToUserIDs)
|
||||
if len(c.config.Share.IMAdminUserID) > 0 {
|
||||
ctx = mcontext.WithOpUserIDContext(ctx, c.config.Share.IMAdminUserID[0])
|
||||
}
|
||||
defer func(groupID string) {
|
||||
if err = c.groupRpcClient.DismissGroup(ctx, groupID); err != nil {
|
||||
log.ZError(ctx, "DismissGroup Notification clear members", err, "groupID", groupID)
|
||||
}
|
||||
}(groupID)
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *ConsumerHandler) offlinePushMsg(ctx context.Context, msg *sdkws.MsgData, offlinePushUserIDs []string) error {
|
||||
title, content, opts, err := c.getOfflinePushInfos(msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.offlinePusher.Push(ctx, offlinePushUserIDs, title, content, opts)
|
||||
if err != nil {
|
||||
prommetrics.MsgOfflinePushFailedCounter.Inc()
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ConsumerHandler) filterGroupMessageOfflinePush(ctx context.Context, groupID string, msg *sdkws.MsgData,
|
||||
offlinePushUserIDs []string) (userIDs []string, err error) {
|
||||
|
||||
//todo local cache Obtain the difference set through local comparison.
|
||||
needOfflinePushUserIDs, err := c.conversationRpcClient.GetConversationOfflinePushUserIDs(
|
||||
ctx, conversationutil.GenGroupConversationID(groupID), offlinePushUserIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return needOfflinePushUserIDs, nil
|
||||
}
|
||||
|
||||
func (c *ConsumerHandler) getOfflinePushInfos(msg *sdkws.MsgData) (title, content string, opts *options.Opts, err error) {
|
||||
type AtTextElem struct {
|
||||
Text string `json:"text,omitempty"`
|
||||
AtUserList []string `json:"atUserList,omitempty"`
|
||||
IsAtSelf bool `json:"isAtSelf"`
|
||||
}
|
||||
|
||||
opts = &options.Opts{Signal: &options.Signal{}}
|
||||
if msg.OfflinePushInfo != nil {
|
||||
opts.IOSBadgeCount = msg.OfflinePushInfo.IOSBadgeCount
|
||||
opts.IOSPushSound = msg.OfflinePushInfo.IOSPushSound
|
||||
opts.Ex = msg.OfflinePushInfo.Ex
|
||||
}
|
||||
|
||||
if msg.OfflinePushInfo != nil {
|
||||
title = msg.OfflinePushInfo.Title
|
||||
content = msg.OfflinePushInfo.Desc
|
||||
}
|
||||
if title == "" {
|
||||
switch msg.ContentType {
|
||||
case constant.Text:
|
||||
fallthrough
|
||||
case constant.Picture:
|
||||
fallthrough
|
||||
case constant.Voice:
|
||||
fallthrough
|
||||
case constant.Video:
|
||||
fallthrough
|
||||
case constant.File:
|
||||
title = constant.ContentType2PushContent[int64(msg.ContentType)]
|
||||
case constant.AtText:
|
||||
ac := AtTextElem{}
|
||||
_ = jsonutil.JsonStringToStruct(string(msg.Content), &ac)
|
||||
case constant.SignalingNotification:
|
||||
title = constant.ContentType2PushContent[constant.SignalMsg]
|
||||
default:
|
||||
title = constant.ContentType2PushContent[constant.Common]
|
||||
}
|
||||
}
|
||||
if content == "" {
|
||||
content = title
|
||||
}
|
||||
return
|
||||
}
|
||||
func (c *ConsumerHandler) DeleteMemberAndSetConversationSeq(ctx context.Context, groupID string, userIDs []string) error {
|
||||
conversationID := msgprocessor.GetConversationIDBySessionType(constant.ReadGroupChatType, groupID)
|
||||
maxSeq, err := c.msgRpcClient.GetConversationMaxSeq(ctx, conversationID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.conversationRpcClient.SetConversationMaxSeq(ctx, userIDs, conversationID, maxSeq)
|
||||
}
|
||||
func unmarshalNotificationElem(bytes []byte, t any) error {
|
||||
var notification sdkws.NotificationElem
|
||||
if err := json.Unmarshal(bytes, ¬ification); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return json.Unmarshal([]byte(notification.Detail), t)
|
||||
}
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package push
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
pbpush "github.com/OpenIMSDK/protocol/push"
|
||||
"github.com/OpenIMSDK/tools/discoveryregistry"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpccache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type pushServer struct {
|
||||
pusher *Pusher
|
||||
config *config.GlobalConfig
|
||||
}
|
||||
|
||||
func Start(config *config.GlobalConfig, client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error {
|
||||
rdb, err := cache.NewRedis(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cacheModel := cache.NewMsgCacheModel(rdb, config)
|
||||
offlinePusher := NewOfflinePusher(config, cacheModel)
|
||||
database := controller.NewPushDatabase(cacheModel)
|
||||
groupRpcClient := rpcclient.NewGroupRpcClient(client, config)
|
||||
conversationRpcClient := rpcclient.NewConversationRpcClient(client, config)
|
||||
msgRpcClient := rpcclient.NewMessageRpcClient(client, config)
|
||||
pusher := NewPusher(
|
||||
config,
|
||||
client,
|
||||
offlinePusher,
|
||||
database,
|
||||
rpccache.NewGroupLocalCache(groupRpcClient, rdb),
|
||||
rpccache.NewConversationLocalCache(conversationRpcClient, rdb),
|
||||
&conversationRpcClient,
|
||||
&groupRpcClient,
|
||||
&msgRpcClient,
|
||||
)
|
||||
|
||||
pbpush.RegisterPushMsgServiceServer(server, &pushServer{
|
||||
pusher: pusher,
|
||||
config: config,
|
||||
})
|
||||
|
||||
consumer, err := NewConsumer(config, pusher)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
consumer.Start()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *pushServer) PushMsg(ctx context.Context, pbData *pbpush.PushMsgReq) (resp *pbpush.PushMsgResp, err error) {
|
||||
switch pbData.MsgData.SessionType {
|
||||
case constant.SuperGroupChatType:
|
||||
err = r.pusher.Push2SuperGroup(ctx, pbData.MsgData.GroupID, pbData.MsgData)
|
||||
default:
|
||||
var pushUserIDList []string
|
||||
isSenderSync := utils.GetSwitchFromOptions(pbData.MsgData.Options, constant.IsSenderSync)
|
||||
if !isSenderSync {
|
||||
pushUserIDList = append(pushUserIDList, pbData.MsgData.RecvID)
|
||||
} else {
|
||||
pushUserIDList = append(pushUserIDList, pbData.MsgData.RecvID, pbData.MsgData.SendID)
|
||||
}
|
||||
err = r.pusher.Push2User(ctx, pushUserIDList, pbData.MsgData)
|
||||
}
|
||||
if err != nil {
|
||||
if err != errNoOfflinePusher {
|
||||
return nil, err
|
||||
}
|
||||
log.ZWarn(ctx, "offline push failed", err, "msg", pbData.String())
|
||||
}
|
||||
return &pbpush.PushMsgResp{}, nil
|
||||
}
|
||||
|
||||
func (r *pushServer) DelUserPushToken(
|
||||
ctx context.Context,
|
||||
req *pbpush.DelUserPushTokenReq,
|
||||
) (resp *pbpush.DelUserPushTokenResp, err error) {
|
||||
if err = r.pusher.database.DelFcmToken(ctx, req.UserID, int(req.PlatformID)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pbpush.DelUserPushTokenResp{}, nil
|
||||
}
|
||||
@@ -1,522 +0,0 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package push
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/OpenIMSDK/protocol/conversation"
|
||||
"github.com/OpenIMSDK/protocol/msggateway"
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
"github.com/OpenIMSDK/tools/discoveryregistry"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/OpenIMSDK/tools/mcontext"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/dummy"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/fcm"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/getui"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/jpush"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpccache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type Pusher struct {
|
||||
config *config.GlobalConfig
|
||||
database controller.PushDatabase
|
||||
discov discoveryregistry.SvcDiscoveryRegistry
|
||||
offlinePusher offlinepush.OfflinePusher
|
||||
groupLocalCache *rpccache.GroupLocalCache
|
||||
conversationLocalCache *rpccache.ConversationLocalCache
|
||||
msgRpcClient *rpcclient.MessageRpcClient
|
||||
conversationRpcClient *rpcclient.ConversationRpcClient
|
||||
groupRpcClient *rpcclient.GroupRpcClient
|
||||
}
|
||||
|
||||
var errNoOfflinePusher = errors.New("no offlinePusher is configured")
|
||||
|
||||
func NewPusher(config *config.GlobalConfig, discov discoveryregistry.SvcDiscoveryRegistry, offlinePusher offlinepush.OfflinePusher, database controller.PushDatabase,
|
||||
groupLocalCache *rpccache.GroupLocalCache, conversationLocalCache *rpccache.ConversationLocalCache,
|
||||
conversationRpcClient *rpcclient.ConversationRpcClient, groupRpcClient *rpcclient.GroupRpcClient, msgRpcClient *rpcclient.MessageRpcClient,
|
||||
) *Pusher {
|
||||
return &Pusher{
|
||||
config: config,
|
||||
discov: discov,
|
||||
database: database,
|
||||
offlinePusher: offlinePusher,
|
||||
groupLocalCache: groupLocalCache,
|
||||
conversationLocalCache: conversationLocalCache,
|
||||
msgRpcClient: msgRpcClient,
|
||||
conversationRpcClient: conversationRpcClient,
|
||||
groupRpcClient: groupRpcClient,
|
||||
}
|
||||
}
|
||||
|
||||
func NewOfflinePusher(config *config.GlobalConfig, cache cache.MsgModel) offlinepush.OfflinePusher {
|
||||
var offlinePusher offlinepush.OfflinePusher
|
||||
switch config.Push.Enable {
|
||||
case "getui":
|
||||
offlinePusher = getui.NewClient(config, cache)
|
||||
case "fcm":
|
||||
offlinePusher = fcm.NewClient(config, cache)
|
||||
case "jpush":
|
||||
offlinePusher = jpush.NewClient(config)
|
||||
default:
|
||||
offlinePusher = dummy.NewClient()
|
||||
}
|
||||
return offlinePusher
|
||||
}
|
||||
|
||||
func (p *Pusher) DeleteMemberAndSetConversationSeq(ctx context.Context, groupID string, userIDs []string) error {
|
||||
conevrsationID := msgprocessor.GetConversationIDBySessionType(constant.SuperGroupChatType, groupID)
|
||||
maxSeq, err := p.msgRpcClient.GetConversationMaxSeq(ctx, conevrsationID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return p.conversationRpcClient.SetConversationMaxSeq(ctx, userIDs, conevrsationID, maxSeq)
|
||||
}
|
||||
|
||||
func (p *Pusher) Push2User(ctx context.Context, userIDs []string, msg *sdkws.MsgData) error {
|
||||
log.ZDebug(ctx, "Get msg from msg_transfer And push msg", "userIDs", userIDs, "msg", msg.String())
|
||||
if err := callbackOnlinePush(ctx, p.config, userIDs, msg); err != nil {
|
||||
return err
|
||||
}
|
||||
// push
|
||||
wsResults, err := p.GetConnsAndOnlinePush(ctx, msg, userIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
isOfflinePush := utils.GetSwitchFromOptions(msg.Options, constant.IsOfflinePush)
|
||||
log.ZDebug(ctx, "push_result", "ws push result", wsResults, "sendData", msg, "isOfflinePush", isOfflinePush, "push_to_userID", userIDs)
|
||||
|
||||
if !isOfflinePush {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(wsResults) == 0 {
|
||||
return nil
|
||||
}
|
||||
onlinePushSuccUserIDSet := utils.SliceSet(utils.Filter(wsResults, func(e *msggateway.SingleMsgToUserResults) (string, bool) {
|
||||
return e.UserID, e.OnlinePush && e.UserID != ""
|
||||
}))
|
||||
offlinePushUserIDList := utils.Filter(wsResults, func(e *msggateway.SingleMsgToUserResults) (string, bool) {
|
||||
_, exist := onlinePushSuccUserIDSet[e.UserID]
|
||||
return e.UserID, !exist && e.UserID != "" && e.UserID != msg.SendID
|
||||
})
|
||||
|
||||
if len(offlinePushUserIDList) > 0 {
|
||||
if err = callbackOfflinePush(ctx, p.config, offlinePushUserIDList, msg, &[]string{}); err != nil {
|
||||
return err
|
||||
}
|
||||
err = p.offlinePushMsg(ctx, msg.SendID, msg, offlinePushUserIDList)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Pusher) UnmarshalNotificationElem(bytes []byte, t any) error {
|
||||
var notification sdkws.NotificationElem
|
||||
if err := json.Unmarshal(bytes, ¬ification); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return json.Unmarshal([]byte(notification.Detail), t)
|
||||
}
|
||||
|
||||
/*
|
||||
k8s deployment,offline push group messages function.
|
||||
*/
|
||||
func (p *Pusher) k8sOfflinePush2SuperGroup(ctx context.Context, groupID string, msg *sdkws.MsgData, wsResults []*msggateway.SingleMsgToUserResults) error {
|
||||
|
||||
var needOfflinePushUserIDs []string
|
||||
for _, v := range wsResults {
|
||||
if !v.OnlinePush {
|
||||
needOfflinePushUserIDs = append(needOfflinePushUserIDs, v.UserID)
|
||||
}
|
||||
}
|
||||
if len(needOfflinePushUserIDs) > 0 {
|
||||
var offlinePushUserIDs []string
|
||||
err := callbackOfflinePush(ctx, p.config, needOfflinePushUserIDs, msg, &offlinePushUserIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(offlinePushUserIDs) > 0 {
|
||||
needOfflinePushUserIDs = offlinePushUserIDs
|
||||
}
|
||||
if msg.ContentType != constant.SignalingNotification {
|
||||
resp, err := p.conversationRpcClient.Client.GetConversationOfflinePushUserIDs(
|
||||
ctx,
|
||||
&conversation.GetConversationOfflinePushUserIDsReq{ConversationID: utils.GenGroupConversationID(groupID), UserIDs: needOfflinePushUserIDs},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(resp.UserIDs) > 0 {
|
||||
err = p.offlinePushMsg(ctx, groupID, msg, resp.UserIDs)
|
||||
if err != nil {
|
||||
log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws.MsgData) (err error) {
|
||||
log.ZDebug(ctx, "Get super group msg from msg_transfer and push msg", "msg", msg.String(), "groupID", groupID)
|
||||
var pushToUserIDs []string
|
||||
if err = callbackBeforeSuperGroupOnlinePush(ctx, p.config, groupID, msg, &pushToUserIDs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(pushToUserIDs) == 0 {
|
||||
pushToUserIDs, err = p.groupLocalCache.GetGroupMemberIDs(ctx, groupID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch msg.ContentType {
|
||||
case constant.MemberQuitNotification:
|
||||
var tips sdkws.MemberQuitTips
|
||||
if p.UnmarshalNotificationElem(msg.Content, &tips) != nil {
|
||||
return err
|
||||
}
|
||||
defer func(groupID string, userIDs []string) {
|
||||
if err = p.DeleteMemberAndSetConversationSeq(ctx, groupID, userIDs); err != nil {
|
||||
log.ZError(ctx, "MemberQuitNotification DeleteMemberAndSetConversationSeq", err, "groupID", groupID, "userIDs", userIDs)
|
||||
}
|
||||
}(groupID, []string{tips.QuitUser.UserID})
|
||||
pushToUserIDs = append(pushToUserIDs, tips.QuitUser.UserID)
|
||||
case constant.MemberKickedNotification:
|
||||
var tips sdkws.MemberKickedTips
|
||||
if p.UnmarshalNotificationElem(msg.Content, &tips) != nil {
|
||||
return err
|
||||
}
|
||||
kickedUsers := utils.Slice(tips.KickedUserList, func(e *sdkws.GroupMemberFullInfo) string { return e.UserID })
|
||||
defer func(groupID string, userIDs []string) {
|
||||
if err = p.DeleteMemberAndSetConversationSeq(ctx, groupID, userIDs); err != nil {
|
||||
log.ZError(ctx, "MemberKickedNotification DeleteMemberAndSetConversationSeq", err, "groupID", groupID, "userIDs", userIDs)
|
||||
}
|
||||
}(groupID, kickedUsers)
|
||||
pushToUserIDs = append(pushToUserIDs, kickedUsers...)
|
||||
case constant.GroupDismissedNotification:
|
||||
// Messages arrive first, notifications arrive later
|
||||
if msgprocessor.IsNotification(msgprocessor.GetConversationIDByMsg(msg)) {
|
||||
var tips sdkws.GroupDismissedTips
|
||||
if p.UnmarshalNotificationElem(msg.Content, &tips) != nil {
|
||||
return err
|
||||
}
|
||||
log.ZInfo(ctx, "GroupDismissedNotificationInfo****", "groupID", groupID, "num", len(pushToUserIDs), "list", pushToUserIDs)
|
||||
if len(p.config.Manager.UserID) > 0 {
|
||||
ctx = mcontext.WithOpUserIDContext(ctx, p.config.Manager.UserID[0])
|
||||
}
|
||||
if len(p.config.Manager.UserID) == 0 && len(p.config.IMAdmin.UserID) > 0 {
|
||||
ctx = mcontext.WithOpUserIDContext(ctx, p.config.IMAdmin.UserID[0])
|
||||
}
|
||||
defer func(groupID string) {
|
||||
if err = p.groupRpcClient.DismissGroup(ctx, groupID); err != nil {
|
||||
log.ZError(ctx, "DismissGroup Notification clear members", err, "groupID", groupID)
|
||||
}
|
||||
}(groupID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wsResults, err := p.GetConnsAndOnlinePush(ctx, msg, pushToUserIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.ZDebug(ctx, "get conn and online push success", "result", wsResults, "msg", msg)
|
||||
isOfflinePush := utils.GetSwitchFromOptions(msg.Options, constant.IsOfflinePush)
|
||||
if isOfflinePush && p.config.Envs.Discovery == "k8s" {
|
||||
return p.k8sOfflinePush2SuperGroup(ctx, groupID, msg, wsResults)
|
||||
}
|
||||
if isOfflinePush && p.config.Envs.Discovery == "zookeeper" {
|
||||
var (
|
||||
onlineSuccessUserIDs = []string{msg.SendID}
|
||||
webAndPcBackgroundUserIDs []string
|
||||
)
|
||||
|
||||
for _, v := range wsResults {
|
||||
if v.OnlinePush && v.UserID != msg.SendID {
|
||||
onlineSuccessUserIDs = append(onlineSuccessUserIDs, v.UserID)
|
||||
}
|
||||
|
||||
if v.OnlinePush {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(v.Resp) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, singleResult := range v.Resp {
|
||||
if singleResult.ResultCode != -2 {
|
||||
continue
|
||||
}
|
||||
|
||||
isPC := constant.PlatformIDToName(int(singleResult.RecvPlatFormID)) == constant.TerminalPC
|
||||
isWebID := singleResult.RecvPlatFormID == constant.WebPlatformID
|
||||
|
||||
if isPC || isWebID {
|
||||
webAndPcBackgroundUserIDs = append(webAndPcBackgroundUserIDs, v.UserID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
needOfflinePushUserIDs := utils.DifferenceString(onlineSuccessUserIDs, pushToUserIDs)
|
||||
|
||||
// Use offline push messaging
|
||||
if len(needOfflinePushUserIDs) > 0 {
|
||||
var offlinePushUserIDs []string
|
||||
err = callbackOfflinePush(ctx, p.config, needOfflinePushUserIDs, msg, &offlinePushUserIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(offlinePushUserIDs) > 0 {
|
||||
needOfflinePushUserIDs = offlinePushUserIDs
|
||||
}
|
||||
if msg.ContentType != constant.SignalingNotification {
|
||||
resp, err := p.conversationRpcClient.Client.GetConversationOfflinePushUserIDs(
|
||||
ctx,
|
||||
&conversation.GetConversationOfflinePushUserIDsReq{ConversationID: utils.GenGroupConversationID(groupID), UserIDs: needOfflinePushUserIDs},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(resp.UserIDs) > 0 {
|
||||
err = p.offlinePushMsg(ctx, groupID, msg, resp.UserIDs)
|
||||
if err != nil {
|
||||
log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg)
|
||||
return err
|
||||
}
|
||||
if _, err := p.GetConnsAndOnlinePush(ctx, msg, utils.IntersectString(resp.UserIDs, webAndPcBackgroundUserIDs)); err != nil {
|
||||
log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg, "userIDs", utils.IntersectString(needOfflinePushUserIDs, webAndPcBackgroundUserIDs))
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Pusher) k8sOnlinePush(ctx context.Context, msg *sdkws.MsgData, pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) {
|
||||
var usersHost = make(map[string][]string)
|
||||
for _, v := range pushToUserIDs {
|
||||
tHost, err := p.discov.GetUserIdHashGatewayHost(ctx, v)
|
||||
if err != nil {
|
||||
log.ZError(ctx, "get msggateway hash error", err)
|
||||
return nil, err
|
||||
}
|
||||
tUsers, tbl := usersHost[tHost]
|
||||
if tbl {
|
||||
tUsers = append(tUsers, v)
|
||||
usersHost[tHost] = tUsers
|
||||
} else {
|
||||
usersHost[tHost] = []string{v}
|
||||
}
|
||||
}
|
||||
log.ZDebug(ctx, "genUsers send hosts struct:", "usersHost", usersHost)
|
||||
var usersConns = make(map[*grpc.ClientConn][]string)
|
||||
for host, userIds := range usersHost {
|
||||
tconn, _ := p.discov.GetConn(ctx, host)
|
||||
usersConns[tconn] = userIds
|
||||
}
|
||||
var (
|
||||
mu sync.Mutex
|
||||
wg = errgroup.Group{}
|
||||
maxWorkers = p.config.Push.MaxConcurrentWorkers
|
||||
)
|
||||
if maxWorkers < 3 {
|
||||
maxWorkers = 3
|
||||
}
|
||||
wg.SetLimit(maxWorkers)
|
||||
for conn, userIds := range usersConns {
|
||||
tcon := conn
|
||||
tuserIds := userIds
|
||||
wg.Go(func() error {
|
||||
input := &msggateway.OnlineBatchPushOneMsgReq{MsgData: msg, PushToUserIDs: tuserIds}
|
||||
msgClient := msggateway.NewMsgGatewayClient(tcon)
|
||||
reply, err := msgClient.SuperGroupOnlineBatchPushOneMsg(ctx, input)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
log.ZDebug(ctx, "push result", "reply", reply)
|
||||
if reply != nil && reply.SinglePushResult != nil {
|
||||
mu.Lock()
|
||||
wsResults = append(wsResults, reply.SinglePushResult...)
|
||||
mu.Unlock()
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
_ = wg.Wait()
|
||||
return wsResults, nil
|
||||
}
|
||||
func (p *Pusher) GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData, pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) {
|
||||
if p.config.Envs.Discovery == "k8s" {
|
||||
return p.k8sOnlinePush(ctx, msg, pushToUserIDs)
|
||||
}
|
||||
conns, err := p.discov.GetConns(ctx, p.config.RpcRegisterName.OpenImMessageGatewayName)
|
||||
log.ZDebug(ctx, "get gateway conn", "conn length", len(conns))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
mu sync.Mutex
|
||||
wg = errgroup.Group{}
|
||||
input = &msggateway.OnlineBatchPushOneMsgReq{MsgData: msg, PushToUserIDs: pushToUserIDs}
|
||||
maxWorkers = p.config.Push.MaxConcurrentWorkers
|
||||
)
|
||||
|
||||
if maxWorkers < 3 {
|
||||
maxWorkers = 3
|
||||
}
|
||||
|
||||
wg.SetLimit(maxWorkers)
|
||||
|
||||
// Online push message
|
||||
for _, conn := range conns {
|
||||
conn := conn // loop var safe
|
||||
wg.Go(func() error {
|
||||
msgClient := msggateway.NewMsgGatewayClient(conn)
|
||||
reply, err := msgClient.SuperGroupOnlineBatchPushOneMsg(ctx, input)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.ZDebug(ctx, "push result", "reply", reply)
|
||||
if reply != nil && reply.SinglePushResult != nil {
|
||||
mu.Lock()
|
||||
wsResults = append(wsResults, reply.SinglePushResult...)
|
||||
mu.Unlock()
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
_ = wg.Wait()
|
||||
|
||||
// always return nil
|
||||
return wsResults, nil
|
||||
}
|
||||
|
||||
func (p *Pusher) offlinePushMsg(ctx context.Context, conversationID string, msg *sdkws.MsgData, offlinePushUserIDs []string) error {
|
||||
title, content, opts, err := p.getOfflinePushInfos(conversationID, msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = p.offlinePusher.Push(ctx, offlinePushUserIDs, title, content, opts)
|
||||
if err != nil {
|
||||
prommetrics.MsgOfflinePushFailedCounter.Inc()
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Pusher) GetOfflinePushOpts(msg *sdkws.MsgData) (opts *offlinepush.Opts, err error) {
|
||||
opts = &offlinepush.Opts{Signal: &offlinepush.Signal{}}
|
||||
// if msg.ContentType > constant.SignalingNotificationBegin && msg.ContentType < constant.SignalingNotificationEnd {
|
||||
// req := &sdkws.SignalReq{}
|
||||
// if err := proto.Unmarshal(msg.Content, req); err != nil {
|
||||
// return nil, utils.Wrap(err, "")
|
||||
// }
|
||||
// switch req.Payload.(type) {
|
||||
// case *sdkws.SignalReq_Invite, *sdkws.SignalReq_InviteInGroup:
|
||||
// opts.Signal = &offlinepush.Signal{ClientMsgID: msg.ClientMsgID}
|
||||
// }
|
||||
// }
|
||||
if msg.OfflinePushInfo != nil {
|
||||
opts.IOSBadgeCount = msg.OfflinePushInfo.IOSBadgeCount
|
||||
opts.IOSPushSound = msg.OfflinePushInfo.IOSPushSound
|
||||
opts.Ex = msg.OfflinePushInfo.Ex
|
||||
}
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
func (p *Pusher) getOfflinePushInfos(conversationID string, msg *sdkws.MsgData) (title, content string, opts *offlinepush.Opts, err error) {
|
||||
if p.offlinePusher == nil {
|
||||
err = errNoOfflinePusher
|
||||
return
|
||||
}
|
||||
|
||||
type atContent struct {
|
||||
Text string `json:"text"`
|
||||
AtUserList []string `json:"atUserList"`
|
||||
IsAtSelf bool `json:"isAtSelf"`
|
||||
}
|
||||
|
||||
opts, err = p.GetOfflinePushOpts(msg)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if msg.OfflinePushInfo != nil {
|
||||
title = msg.OfflinePushInfo.Title
|
||||
content = msg.OfflinePushInfo.Desc
|
||||
}
|
||||
if title == "" {
|
||||
switch msg.ContentType {
|
||||
case constant.Text:
|
||||
fallthrough
|
||||
case constant.Picture:
|
||||
fallthrough
|
||||
case constant.Voice:
|
||||
fallthrough
|
||||
case constant.Video:
|
||||
fallthrough
|
||||
case constant.File:
|
||||
title = constant.ContentType2PushContent[int64(msg.ContentType)]
|
||||
case constant.AtText:
|
||||
ac := atContent{}
|
||||
_ = utils.JsonStringToStruct(string(msg.Content), &ac)
|
||||
if utils.IsContain(conversationID, ac.AtUserList) {
|
||||
title = constant.ContentType2PushContent[constant.AtText] + constant.ContentType2PushContent[constant.Common]
|
||||
} else {
|
||||
title = constant.ContentType2PushContent[constant.GroupMsg]
|
||||
}
|
||||
case constant.SignalingNotification:
|
||||
title = constant.ContentType2PushContent[constant.SignalMsg]
|
||||
default:
|
||||
title = constant.ContentType2PushContent[constant.Common]
|
||||
}
|
||||
}
|
||||
if content == "" {
|
||||
content = title
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package push
|
||||
|
||||
import (
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func GetContent(msg *sdkws.MsgData) string {
|
||||
if msg.ContentType >= constant.NotificationBegin && msg.ContentType <= constant.NotificationEnd {
|
||||
var tips sdkws.TipsComm
|
||||
_ = proto.Unmarshal(msg.Content, &tips)
|
||||
content := tips.JsonDetail
|
||||
return content
|
||||
}
|
||||
return string(msg.Content)
|
||||
}
|
||||
+66
-34
@@ -16,45 +16,54 @@ package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
pbauth "github.com/OpenIMSDK/protocol/auth"
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/OpenIMSDK/protocol/msggateway"
|
||||
"github.com/OpenIMSDK/tools/discoveryregistry"
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/OpenIMSDK/tools/mcontext"
|
||||
"github.com/OpenIMSDK/tools/tokenverify"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/tools/db/redisutil"
|
||||
"github.com/redis/go-redis/v9"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
pbauth "github.com/openimsdk/protocol/auth"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
"github.com/openimsdk/protocol/msggateway"
|
||||
"github.com/openimsdk/tools/discovery"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/mcontext"
|
||||
"github.com/openimsdk/tools/tokenverify"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type authServer struct {
|
||||
authDatabase controller.AuthDatabase
|
||||
userRpcClient *rpcclient.UserRpcClient
|
||||
RegisterCenter discoveryregistry.SvcDiscoveryRegistry
|
||||
config *config.GlobalConfig
|
||||
RegisterCenter discovery.SvcDiscoveryRegistry
|
||||
config *Config
|
||||
}
|
||||
|
||||
func Start(config *config.GlobalConfig, client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error {
|
||||
rdb, err := cache.NewRedis(config)
|
||||
type Config struct {
|
||||
RpcConfig config.Auth
|
||||
RedisConfig config.Redis
|
||||
ZookeeperConfig config.ZooKeeper
|
||||
Share config.Share
|
||||
}
|
||||
|
||||
func Start(ctx context.Context, config *Config, client discovery.SvcDiscoveryRegistry, server *grpc.Server) error {
|
||||
rdb, err := redisutil.NewRedisClient(ctx, config.RedisConfig.Build())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
userRpcClient := rpcclient.NewUserRpcClient(client, config)
|
||||
userRpcClient := rpcclient.NewUserRpcClient(client, config.Share.RpcRegisterName.User, config.Share.IMAdminUserID)
|
||||
pbauth.RegisterAuthServer(server, &authServer{
|
||||
userRpcClient: &userRpcClient,
|
||||
RegisterCenter: client,
|
||||
authDatabase: controller.NewAuthDatabase(
|
||||
cache.NewMsgCacheModel(rdb, config),
|
||||
config.Secret,
|
||||
config.TokenPolicy.Expire,
|
||||
config,
|
||||
cache.NewTokenCacheModel(rdb),
|
||||
config.Share.Secret,
|
||||
config.RpcConfig.TokenPolicy.Expire,
|
||||
),
|
||||
config: config,
|
||||
})
|
||||
@@ -63,8 +72,8 @@ func Start(config *config.GlobalConfig, client discoveryregistry.SvcDiscoveryReg
|
||||
|
||||
func (s *authServer) UserToken(ctx context.Context, req *pbauth.UserTokenReq) (*pbauth.UserTokenResp, error) {
|
||||
resp := pbauth.UserTokenResp{}
|
||||
if req.Secret != s.config.Secret {
|
||||
return nil, errs.ErrNoPermission.Wrap("secret invalid")
|
||||
if req.Secret != s.config.Share.Secret {
|
||||
return nil, errs.ErrNoPermission.WrapMsg("secret invalid")
|
||||
}
|
||||
if _, err := s.userRpcClient.GetUserInfo(ctx, req.UserID); err != nil {
|
||||
return nil, err
|
||||
@@ -75,20 +84,19 @@ func (s *authServer) UserToken(ctx context.Context, req *pbauth.UserTokenReq) (*
|
||||
}
|
||||
prommetrics.UserLoginCounter.Inc()
|
||||
resp.Token = token
|
||||
resp.ExpireTimeSeconds = s.config.TokenPolicy.Expire * 24 * 60 * 60
|
||||
resp.ExpireTimeSeconds = s.config.RpcConfig.TokenPolicy.Expire * 24 * 60 * 60
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (s *authServer) GetUserToken(ctx context.Context, req *pbauth.GetUserTokenReq) (*pbauth.GetUserTokenResp, error) {
|
||||
if err := authverify.CheckAdmin(ctx, s.config); err != nil {
|
||||
if err := authverify.CheckAdmin(ctx, s.config.Share.IMAdminUserID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp := pbauth.GetUserTokenResp{}
|
||||
|
||||
if authverify.IsManagerUserID(req.UserID, s.config) {
|
||||
return nil, errs.ErrNoPermission.Wrap("don't get Admin token")
|
||||
if authverify.IsManagerUserID(req.UserID, s.config.Share.IMAdminUserID) {
|
||||
return nil, errs.ErrNoPermission.WrapMsg("don't get Admin token")
|
||||
}
|
||||
|
||||
if _, err := s.userRpcClient.GetUserInfo(ctx, req.UserID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -97,12 +105,12 @@ func (s *authServer) GetUserToken(ctx context.Context, req *pbauth.GetUserTokenR
|
||||
return nil, err
|
||||
}
|
||||
resp.Token = token
|
||||
resp.ExpireTimeSeconds = s.config.TokenPolicy.Expire * 24 * 60 * 60
|
||||
resp.ExpireTimeSeconds = s.config.RpcConfig.TokenPolicy.Expire * 24 * 60 * 60
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (s *authServer) parseToken(ctx context.Context, tokensString string) (claims *tokenverify.Claims, err error) {
|
||||
claims, err = tokenverify.GetClaimFromToken(tokensString, authverify.Secret(s.config.Secret))
|
||||
claims, err = tokenverify.GetClaimFromToken(tokensString, authverify.Secret(s.config.Share.Secret))
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
@@ -111,19 +119,19 @@ func (s *authServer) parseToken(ctx context.Context, tokensString string) (claim
|
||||
return nil, err
|
||||
}
|
||||
if len(m) == 0 {
|
||||
return nil, errs.ErrTokenNotExist.Wrap()
|
||||
return nil, servererrs.ErrTokenNotExist.Wrap()
|
||||
}
|
||||
if v, ok := m[tokensString]; ok {
|
||||
switch v {
|
||||
case constant.NormalToken:
|
||||
return claims, nil
|
||||
case constant.KickedToken:
|
||||
return nil, errs.ErrTokenKicked.Wrap()
|
||||
return nil, servererrs.ErrTokenKicked.Wrap()
|
||||
default:
|
||||
return nil, errs.Wrap(errs.ErrTokenUnknown)
|
||||
}
|
||||
}
|
||||
return nil, errs.ErrTokenNotExist.Wrap()
|
||||
return nil, servererrs.ErrTokenNotExist.Wrap()
|
||||
}
|
||||
|
||||
func (s *authServer) ParseToken(
|
||||
@@ -136,13 +144,13 @@ func (s *authServer) ParseToken(
|
||||
return nil, err
|
||||
}
|
||||
resp.UserID = claims.UserID
|
||||
resp.Platform = constant.PlatformIDToName(claims.PlatformID)
|
||||
resp.PlatformID = int32(claims.PlatformID)
|
||||
resp.ExpireTimeSeconds = claims.ExpiresAt.Unix()
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (s *authServer) ForceLogout(ctx context.Context, req *pbauth.ForceLogoutReq) (*pbauth.ForceLogoutResp, error) {
|
||||
if err := authverify.CheckAdmin(ctx, s.config); err != nil {
|
||||
if err := authverify.CheckAdmin(ctx, s.config.Share.IMAdminUserID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.forceKickOff(ctx, req.UserID, req.PlatformID, mcontext.GetOperationID(ctx)); err != nil {
|
||||
@@ -152,7 +160,7 @@ func (s *authServer) ForceLogout(ctx context.Context, req *pbauth.ForceLogoutReq
|
||||
}
|
||||
|
||||
func (s *authServer) forceKickOff(ctx context.Context, userID string, platformID int32, operationID string) error {
|
||||
conns, err := s.RegisterCenter.GetConns(ctx, s.config.RpcRegisterName.OpenImMessageGatewayName)
|
||||
conns, err := s.RegisterCenter.GetConns(ctx, s.config.Share.RpcRegisterName.MessageGateway)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -169,3 +177,27 @@ func (s *authServer) forceKickOff(ctx context.Context, userID string, platformID
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (s *authServer) InvalidateToken(ctx context.Context, req *pbauth.InvalidateTokenReq) (*pbauth.InvalidateTokenResp, error) {
|
||||
m, err := s.authDatabase.GetTokensWithoutError(ctx, req.UserID, int(req.PlatformID))
|
||||
if err != nil && err != redis.Nil {
|
||||
return nil, err
|
||||
}
|
||||
if m == nil {
|
||||
return nil, errs.New("token map is empty").Wrap()
|
||||
}
|
||||
log.ZDebug(ctx, "get token from redis", "userID", req.UserID, "platformID",
|
||||
req.PlatformID, "tokenMap", m)
|
||||
|
||||
for k := range m {
|
||||
if k != req.GetPreservedToken() {
|
||||
m[k] = constant.KickedToken
|
||||
}
|
||||
}
|
||||
log.ZDebug(ctx, "set token map is ", "token map", m, "userID",
|
||||
req.UserID, "token", req.GetPreservedToken())
|
||||
err = s.authDatabase.SetTokenMapByUidPid(ctx, req.UserID, int(req.PlatformID), m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pbauth.InvalidateTokenResp{}, nil
|
||||
}
|
||||
|
||||
@@ -16,26 +16,25 @@ package conversation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/tools/db/redisutil"
|
||||
"sort"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
pbconversation "github.com/OpenIMSDK/protocol/conversation"
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
"github.com/OpenIMSDK/tools/discoveryregistry"
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/OpenIMSDK/tools/tx"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/convert"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo"
|
||||
tablerelation "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient/notification"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
pbconversation "github.com/openimsdk/protocol/conversation"
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"github.com/openimsdk/tools/discovery"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
@@ -44,33 +43,43 @@ type conversationServer struct {
|
||||
user *rpcclient.UserRpcClient
|
||||
groupRpcClient *rpcclient.GroupRpcClient
|
||||
conversationDatabase controller.ConversationDatabase
|
||||
conversationNotificationSender *notification.ConversationNotificationSender
|
||||
config *config.GlobalConfig
|
||||
conversationNotificationSender *ConversationNotificationSender
|
||||
config *Config
|
||||
}
|
||||
|
||||
func Start(config *config.GlobalConfig, client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error {
|
||||
rdb, err := cache.NewRedis(config)
|
||||
type Config struct {
|
||||
RpcConfig config.Conversation
|
||||
RedisConfig config.Redis
|
||||
MongodbConfig config.Mongo
|
||||
ZookeeperConfig config.ZooKeeper
|
||||
NotificationConfig config.Notification
|
||||
Share config.Share
|
||||
LocalCacheConfig config.LocalCache
|
||||
}
|
||||
|
||||
func Start(ctx context.Context, config *Config, client discovery.SvcDiscoveryRegistry, server *grpc.Server) error {
|
||||
mgocli, err := mongoutil.NewMongoDB(ctx, config.MongodbConfig.Build())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mongo, err := unrelation.NewMongo(config)
|
||||
rdb, err := redisutil.NewRedisClient(ctx, config.RedisConfig.Build())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
conversationDB, err := mgo.NewConversationMongo(mongo.GetDatabase(config.Mongo.Database))
|
||||
conversationDB, err := mgo.NewConversationMongo(mgocli.GetDB())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
groupRpcClient := rpcclient.NewGroupRpcClient(client, config)
|
||||
msgRpcClient := rpcclient.NewMessageRpcClient(client, config)
|
||||
userRpcClient := rpcclient.NewUserRpcClient(client, config)
|
||||
groupRpcClient := rpcclient.NewGroupRpcClient(client, config.Share.RpcRegisterName.Group)
|
||||
msgRpcClient := rpcclient.NewMessageRpcClient(client, config.Share.RpcRegisterName.Msg)
|
||||
userRpcClient := rpcclient.NewUserRpcClient(client, config.Share.RpcRegisterName.User, config.Share.IMAdminUserID)
|
||||
cache.InitLocalCache(&config.LocalCacheConfig)
|
||||
pbconversation.RegisterConversationServer(server, &conversationServer{
|
||||
msgRpcClient: &msgRpcClient,
|
||||
user: &userRpcClient,
|
||||
conversationNotificationSender: notification.NewConversationNotificationSender(config, &msgRpcClient),
|
||||
conversationNotificationSender: NewConversationNotificationSender(&config.NotificationConfig, &msgRpcClient),
|
||||
groupRpcClient: &groupRpcClient,
|
||||
conversationDatabase: controller.NewConversationDatabase(conversationDB, cache.NewConversationRedis(rdb, cache.GetDefaultOpt(), conversationDB), tx.NewMongo(mongo.GetClient())),
|
||||
config: config,
|
||||
conversationDatabase: controller.NewConversationDatabase(conversationDB, cache.NewConversationRedis(rdb, &config.LocalCacheConfig, cache.GetDefaultOpt(), conversationDB), mgocli.GetTx()),
|
||||
})
|
||||
return nil
|
||||
}
|
||||
@@ -81,7 +90,7 @@ func (c *conversationServer) GetConversation(ctx context.Context, req *pbconvers
|
||||
return nil, err
|
||||
}
|
||||
if len(conversations) < 1 {
|
||||
return nil, errs.ErrRecordNotFound.Wrap("conversation not found")
|
||||
return nil, errs.ErrRecordNotFound.WrapMsg("conversation not found")
|
||||
}
|
||||
resp := &pbconversation.GetConversationResp{Conversation: &pbconversation.Conversation{}}
|
||||
resp.Conversation = convert.ConversationDB2Pb(conversations[0])
|
||||
@@ -158,7 +167,7 @@ func (c *conversationServer) GetSortedConversationList(ctx context.Context, req
|
||||
c.conversationSort(conversation_isPinTime, resp, conversation_unreadCount, conversationMsg)
|
||||
c.conversationSort(conversation_notPinTime, resp, conversation_unreadCount, conversationMsg)
|
||||
|
||||
resp.ConversationElems = utils.Paginate(resp.ConversationElems, int(req.Pagination.GetPageNumber()), int(req.Pagination.GetShowNumber()))
|
||||
resp.ConversationElems = datautil.Paginate(resp.ConversationElems, int(req.Pagination.GetPageNumber()), int(req.Pagination.GetShowNumber()))
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
@@ -184,32 +193,30 @@ func (c *conversationServer) GetConversations(ctx context.Context, req *pbconver
|
||||
|
||||
func (c *conversationServer) SetConversation(ctx context.Context, req *pbconversation.SetConversationReq) (*pbconversation.SetConversationResp, error) {
|
||||
var conversation tablerelation.ConversationModel
|
||||
if err := utils.CopyStructFields(&conversation, req.Conversation); err != nil {
|
||||
if err := datautil.CopyStructFields(&conversation, req.Conversation); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err := c.conversationDatabase.SetUserConversations(ctx, req.Conversation.OwnerUserID, []*tablerelation.ConversationModel{&conversation})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_ = c.conversationNotificationSender.ConversationChangeNotification(ctx, req.Conversation.OwnerUserID, []string{req.Conversation.ConversationID})
|
||||
c.conversationNotificationSender.ConversationChangeNotification(ctx, req.Conversation.OwnerUserID, []string{req.Conversation.ConversationID})
|
||||
resp := &pbconversation.SetConversationResp{}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// nolint
|
||||
func (c *conversationServer) SetConversations(ctx context.Context,
|
||||
req *pbconversation.SetConversationsReq,
|
||||
) (*pbconversation.SetConversationsResp, error) {
|
||||
func (c *conversationServer) SetConversations(ctx context.Context, req *pbconversation.SetConversationsReq) (*pbconversation.SetConversationsResp, error) {
|
||||
if req.Conversation == nil {
|
||||
return nil, errs.ErrArgs.Wrap("conversation must not be nil")
|
||||
return nil, errs.ErrArgs.WrapMsg("conversation must not be nil")
|
||||
}
|
||||
if req.Conversation.ConversationType == constant.GroupChatType {
|
||||
if req.Conversation.ConversationType == constant.WriteGroupChatType {
|
||||
groupInfo, err := c.groupRpcClient.GetGroupInfo(ctx, req.Conversation.GroupID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if groupInfo.Status == constant.GroupStatusDismissed {
|
||||
return nil, errs.ErrDismissedAlready.Wrap("group dismissed")
|
||||
return nil, servererrs.ErrDismissedAlready.WrapMsg("group dismissed")
|
||||
}
|
||||
}
|
||||
var unequal int
|
||||
@@ -220,7 +227,7 @@ func (c *conversationServer) SetConversations(ctx context.Context,
|
||||
return nil, err
|
||||
}
|
||||
if len(cs) == 0 {
|
||||
return nil, errs.ErrRecordNotFound.Wrap("conversation not found")
|
||||
return nil, errs.ErrRecordNotFound.WrapMsg("conversation not found")
|
||||
}
|
||||
conv = *cs[0]
|
||||
}
|
||||
@@ -272,7 +279,7 @@ func (c *conversationServer) SetConversations(ctx context.Context,
|
||||
unequal++
|
||||
}
|
||||
}
|
||||
if req.Conversation.IsPrivateChat != nil && req.Conversation.ConversationType != constant.SuperGroupChatType {
|
||||
if req.Conversation.IsPrivateChat != nil && req.Conversation.ConversationType != constant.ReadGroupChatType {
|
||||
var conversations []*tablerelation.ConversationModel
|
||||
for _, ownerUserID := range req.UserIDs {
|
||||
conversation2 := conversation
|
||||
@@ -280,45 +287,39 @@ func (c *conversationServer) SetConversations(ctx context.Context,
|
||||
conversation2.IsPrivateChat = req.Conversation.IsPrivateChat.Value
|
||||
conversations = append(conversations, &conversation2)
|
||||
}
|
||||
|
||||
if err := c.conversationDatabase.SyncPeerUserPrivateConversationTx(ctx, conversations); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, userID := range req.UserIDs {
|
||||
err := c.conversationNotificationSender.ConversationSetPrivateNotification(ctx, userID, req.Conversation.UserID,
|
||||
c.conversationNotificationSender.ConversationSetPrivateNotification(ctx, userID, req.Conversation.UserID,
|
||||
req.Conversation.IsPrivateChat.Value, req.Conversation.ConversationID)
|
||||
if err != nil {
|
||||
log.ZWarn(ctx, "send conversation set private notification failed", err,
|
||||
"userID", userID, "conversationID", req.Conversation.ConversationID)
|
||||
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if req.Conversation.BurnDuration != nil {
|
||||
m["burn_duration"] = req.Conversation.BurnDuration.Value
|
||||
if req.Conversation.BurnDuration.Value != conv.BurnDuration {
|
||||
unequal++
|
||||
}
|
||||
}
|
||||
|
||||
if err := c.conversationDatabase.SetUsersConversationFieldTx(ctx, req.UserIDs, &conversation, m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if unequal > 0 {
|
||||
for _, v := range req.UserIDs {
|
||||
c.conversationNotificationSender.ConversationChangeNotification(ctx, v, []string{req.Conversation.ConversationID})
|
||||
}
|
||||
}
|
||||
|
||||
return &pbconversation.SetConversationsResp{}, nil
|
||||
}
|
||||
|
||||
// Get user IDs with "Do Not Disturb" enabled in super large groups.
|
||||
func (c *conversationServer) GetRecvMsgNotNotifyUserIDs(ctx context.Context, req *pbconversation.GetRecvMsgNotNotifyUserIDsReq) (*pbconversation.GetRecvMsgNotNotifyUserIDsResp, error) {
|
||||
//userIDs, err := c.conversationDatabase.FindRecvMsgNotNotifyUserIDs(ctx, req.GroupID)
|
||||
//if err != nil {
|
||||
// return nil, err
|
||||
//}
|
||||
//return &pbconversation.GetRecvMsgNotNotifyUserIDsResp{UserIDs: userIDs}, nil
|
||||
return nil, errors.New("deprecated")
|
||||
return nil, errs.New("deprecated")
|
||||
}
|
||||
|
||||
// create conversation without notification for msg redis transfer.
|
||||
@@ -402,12 +403,9 @@ func (c *conversationServer) GetConversationsByConversationID(
|
||||
return &pbconversation.GetConversationsByConversationIDResp{Conversations: convert.ConversationsDB2Pb(conversations)}, nil
|
||||
}
|
||||
|
||||
func (c *conversationServer) GetConversationOfflinePushUserIDs(
|
||||
ctx context.Context,
|
||||
req *pbconversation.GetConversationOfflinePushUserIDsReq,
|
||||
) (*pbconversation.GetConversationOfflinePushUserIDsResp, error) {
|
||||
func (c *conversationServer) GetConversationOfflinePushUserIDs(ctx context.Context, req *pbconversation.GetConversationOfflinePushUserIDsReq) (*pbconversation.GetConversationOfflinePushUserIDsResp, error) {
|
||||
if req.ConversationID == "" {
|
||||
return nil, errs.ErrArgs.Wrap("conversationID is empty")
|
||||
return nil, errs.ErrArgs.WrapMsg("conversationID is empty")
|
||||
}
|
||||
if len(req.UserIDs) == 0 {
|
||||
return &pbconversation.GetConversationOfflinePushUserIDsResp{}, nil
|
||||
@@ -426,21 +424,16 @@ func (c *conversationServer) GetConversationOfflinePushUserIDs(
|
||||
for _, userID := range userIDs {
|
||||
delete(userIDSet, userID)
|
||||
}
|
||||
return &pbconversation.GetConversationOfflinePushUserIDsResp{UserIDs: utils.Keys(userIDSet)}, nil
|
||||
return &pbconversation.GetConversationOfflinePushUserIDsResp{UserIDs: datautil.Keys(userIDSet)}, nil
|
||||
}
|
||||
|
||||
func (c *conversationServer) conversationSort(
|
||||
conversations map[int64]string,
|
||||
resp *pbconversation.GetSortedConversationListResp,
|
||||
conversation_unreadCount map[string]int64,
|
||||
conversationMsg map[string]*pbconversation.ConversationElem,
|
||||
) {
|
||||
func (c *conversationServer) conversationSort(conversations map[int64]string, resp *pbconversation.GetSortedConversationListResp, conversation_unreadCount map[string]int64, conversationMsg map[string]*pbconversation.ConversationElem) {
|
||||
keys := []int64{}
|
||||
for key := range conversations {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
|
||||
sort.Slice(keys[:], func(i, j int) bool {
|
||||
sort.Slice(keys, func(i, j int) bool {
|
||||
return keys[i] > keys[j]
|
||||
})
|
||||
index := 0
|
||||
@@ -474,7 +467,7 @@ func (c *conversationServer) getConversationInfo(
|
||||
sendIDs = append(sendIDs, chatLog.RecvID)
|
||||
}
|
||||
sendIDs = append(sendIDs, chatLog.SendID)
|
||||
case constant.GroupChatType, constant.SuperGroupChatType:
|
||||
case constant.WriteGroupChatType, constant.ReadGroupChatType:
|
||||
groupIDs = append(groupIDs, chatLog.GroupID)
|
||||
sendIDs = append(sendIDs, chatLog.SendID)
|
||||
}
|
||||
@@ -500,7 +493,7 @@ func (c *conversationServer) getConversationInfo(
|
||||
for conversationID, chatLog := range chatLogs {
|
||||
pbchatLog := &pbconversation.ConversationElem{}
|
||||
msgInfo := &pbconversation.MsgInfo{}
|
||||
if err := utils.CopyStructFields(msgInfo, chatLog); err != nil {
|
||||
if err := datautil.CopyStructFields(msgInfo, chatLog); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch chatLog.SessionType {
|
||||
@@ -516,7 +509,7 @@ func (c *conversationServer) getConversationInfo(
|
||||
msgInfo.FaceURL = send.FaceURL
|
||||
msgInfo.SenderName = send.Nickname
|
||||
}
|
||||
case constant.GroupChatType, constant.SuperGroupChatType:
|
||||
case constant.WriteGroupChatType, constant.ReadGroupChatType:
|
||||
msgInfo.GroupID = chatLog.GroupID
|
||||
if group, ok := groupMap[chatLog.GroupID]; ok {
|
||||
msgInfo.GroupName = group.GroupName
|
||||
@@ -536,10 +529,7 @@ func (c *conversationServer) getConversationInfo(
|
||||
return conversationMsg, nil
|
||||
}
|
||||
|
||||
func (c *conversationServer) GetConversationNotReceiveMessageUserIDs(
|
||||
ctx context.Context,
|
||||
req *pbconversation.GetConversationNotReceiveMessageUserIDsReq,
|
||||
) (*pbconversation.GetConversationNotReceiveMessageUserIDsResp, error) {
|
||||
func (c *conversationServer) GetConversationNotReceiveMessageUserIDs(ctx context.Context, req *pbconversation.GetConversationNotReceiveMessageUserIDsReq) (*pbconversation.GetConversationNotReceiveMessageUserIDsResp, error) {
|
||||
userIDs, err := c.conversationDatabase.GetConversationNotReceiveMessageUserIDs(ctx, req.ConversationID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package conversation
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
)
|
||||
|
||||
type ConversationNotificationSender struct {
|
||||
*rpcclient.NotificationSender
|
||||
}
|
||||
|
||||
func NewConversationNotificationSender(conf *config.Notification, msgRpcClient *rpcclient.MessageRpcClient) *ConversationNotificationSender {
|
||||
return &ConversationNotificationSender{rpcclient.NewNotificationSender(conf, rpcclient.WithRpcClient(msgRpcClient))}
|
||||
}
|
||||
|
||||
// SetPrivate invote.
|
||||
func (c *ConversationNotificationSender) ConversationSetPrivateNotification(ctx context.Context, sendID, recvID string,
|
||||
isPrivateChat bool, conversationID string,
|
||||
) {
|
||||
tips := &sdkws.ConversationSetPrivateTips{
|
||||
RecvID: recvID,
|
||||
SendID: sendID,
|
||||
IsPrivate: isPrivateChat,
|
||||
ConversationID: conversationID,
|
||||
}
|
||||
|
||||
c.Notification(ctx, sendID, recvID, constant.ConversationPrivateChatNotification, tips)
|
||||
}
|
||||
|
||||
func (c *ConversationNotificationSender) ConversationChangeNotification(ctx context.Context, userID string, conversationIDs []string) {
|
||||
tips := &sdkws.ConversationUpdateTips{
|
||||
UserID: userID,
|
||||
ConversationIDList: conversationIDs,
|
||||
}
|
||||
|
||||
c.Notification(ctx, userID, userID, constant.ConversationChangeNotification, tips)
|
||||
}
|
||||
|
||||
func (c *ConversationNotificationSender) ConversationUnreadChangeNotification(
|
||||
ctx context.Context,
|
||||
userID, conversationID string,
|
||||
unreadCountTime, hasReadSeq int64,
|
||||
) {
|
||||
tips := &sdkws.ConversationHasReadTips{
|
||||
UserID: userID,
|
||||
ConversationID: conversationID,
|
||||
HasReadSeq: hasReadSeq,
|
||||
UnreadCountTime: unreadCountTime,
|
||||
}
|
||||
|
||||
c.Notification(ctx, userID, userID, constant.ConversationUnreadNotification, tips)
|
||||
}
|
||||
@@ -18,11 +18,11 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
pbfriend "github.com/OpenIMSDK/protocol/friend"
|
||||
"github.com/OpenIMSDK/tools/mcontext"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/convert"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
|
||||
pbfriend "github.com/openimsdk/protocol/friend"
|
||||
"github.com/openimsdk/tools/mcontext"
|
||||
)
|
||||
|
||||
func (s *friendServer) GetPaginationBlacks(ctx context.Context, req *pbfriend.GetPaginationBlacksReq) (resp *pbfriend.GetPaginationBlacksResp, err error) {
|
||||
@@ -57,15 +57,18 @@ func (s *friendServer) RemoveBlack(ctx context.Context, req *pbfriend.RemoveBlac
|
||||
if err := s.userRpcClient.Access(ctx, req.OwnerUserID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := s.blackDatabase.Delete(ctx, []*relation.BlackModel{{OwnerUserID: req.OwnerUserID, BlockUserID: req.BlackUserID}}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.notificationSender.BlackDeletedNotification(ctx, req)
|
||||
|
||||
return &pbfriend.RemoveBlackResp{}, nil
|
||||
}
|
||||
|
||||
func (s *friendServer) AddBlack(ctx context.Context, req *pbfriend.AddBlackReq) (*pbfriend.AddBlackResp, error) {
|
||||
if err := authverify.CheckAccessV3(ctx, req.OwnerUserID, s.config); err != nil {
|
||||
if err := authverify.CheckAccessV3(ctx, req.OwnerUserID, s.config.Share.IMAdminUserID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err := s.userRpcClient.GetUsersInfo(ctx, []string{req.OwnerUserID, req.BlackUserID})
|
||||
@@ -83,10 +86,6 @@ func (s *friendServer) AddBlack(ctx context.Context, req *pbfriend.AddBlackReq)
|
||||
if err := s.blackDatabase.Create(ctx, []*relation.BlackModel{&black}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := s.notificationSender.BlackAddedNotification(ctx, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.notificationSender.BlackAddedNotification(ctx, req)
|
||||
return &pbfriend.AddBlackResp{}, nil
|
||||
}
|
||||
|
||||
+100
-134
@@ -16,85 +16,41 @@ package friend
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/webhook"
|
||||
|
||||
pbfriend "github.com/OpenIMSDK/protocol/friend"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/http"
|
||||
pbfriend "github.com/openimsdk/protocol/friend"
|
||||
)
|
||||
|
||||
func CallbackBeforeAddFriend(ctx context.Context, globalConfig *config.GlobalConfig, req *pbfriend.ApplyToAddFriendReq) error {
|
||||
if !globalConfig.Callback.CallbackBeforeAddFriend.Enable {
|
||||
return nil
|
||||
}
|
||||
cbReq := &cbapi.CallbackBeforeAddFriendReq{
|
||||
CallbackCommand: cbapi.CallbackBeforeAddFriendCommand,
|
||||
FromUserID: req.FromUserID,
|
||||
ToUserID: req.ToUserID,
|
||||
ReqMsg: req.ReqMsg,
|
||||
Ex: req.Ex,
|
||||
}
|
||||
resp := &cbapi.CallbackBeforeAddFriendResp{}
|
||||
if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackBeforeAddFriend); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func CallbackBeforeSetFriendRemark(ctx context.Context, globalConfig *config.GlobalConfig, req *pbfriend.SetFriendRemarkReq) error {
|
||||
if !globalConfig.Callback.CallbackBeforeSetFriendRemark.Enable {
|
||||
return nil
|
||||
}
|
||||
cbReq := &cbapi.CallbackBeforeSetFriendRemarkReq{
|
||||
CallbackCommand: cbapi.CallbackBeforeSetFriendRemark,
|
||||
func (s *friendServer) webhookAfterDeleteFriend(ctx context.Context, after *config.AfterConfig, req *pbfriend.DeleteFriendReq) {
|
||||
cbReq := &cbapi.CallbackAfterDeleteFriendReq{
|
||||
CallbackCommand: cbapi.CallbackAfterDeleteFriendCommand,
|
||||
OwnerUserID: req.OwnerUserID,
|
||||
FriendUserID: req.FriendUserID,
|
||||
Remark: req.Remark,
|
||||
}
|
||||
resp := &cbapi.CallbackBeforeSetFriendRemarkResp{}
|
||||
if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackBeforeAddFriend); err != nil {
|
||||
return err
|
||||
}
|
||||
utils.NotNilReplace(&req.Remark, &resp.Remark)
|
||||
return nil
|
||||
s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &cbapi.CallbackAfterDeleteFriendResp{}, after)
|
||||
}
|
||||
|
||||
func CallbackAfterSetFriendRemark(ctx context.Context, globalConfig *config.GlobalConfig, req *pbfriend.SetFriendRemarkReq) error {
|
||||
if !globalConfig.Callback.CallbackAfterSetFriendRemark.Enable {
|
||||
func (s *friendServer) webhookBeforeAddFriend(ctx context.Context, before *config.BeforeConfig, req *pbfriend.ApplyToAddFriendReq) error {
|
||||
return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
|
||||
cbReq := &cbapi.CallbackBeforeAddFriendReq{
|
||||
CallbackCommand: cbapi.CallbackBeforeAddFriendCommand,
|
||||
FromUserID: req.FromUserID,
|
||||
ToUserID: req.ToUserID,
|
||||
ReqMsg: req.ReqMsg,
|
||||
Ex: req.Ex,
|
||||
}
|
||||
resp := &cbapi.CallbackBeforeAddFriendResp{}
|
||||
|
||||
if err := s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
cbReq := &cbapi.CallbackAfterSetFriendRemarkReq{
|
||||
CallbackCommand: cbapi.CallbackAfterSetFriendRemark,
|
||||
OwnerUserID: req.OwnerUserID,
|
||||
FriendUserID: req.FriendUserID,
|
||||
Remark: req.Remark,
|
||||
}
|
||||
resp := &cbapi.CallbackAfterSetFriendRemarkResp{}
|
||||
if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackBeforeAddFriend); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
func CallbackBeforeAddBlack(ctx context.Context, globalConfig *config.GlobalConfig, req *pbfriend.AddBlackReq) error {
|
||||
if !globalConfig.Callback.CallbackBeforeAddBlack.Enable {
|
||||
return nil
|
||||
}
|
||||
cbReq := &cbapi.CallbackBeforeAddBlackReq{
|
||||
CallbackCommand: cbapi.CallbackBeforeAddBlackCommand,
|
||||
OwnerUserID: req.OwnerUserID,
|
||||
BlackUserID: req.BlackUserID,
|
||||
}
|
||||
resp := &cbapi.CallbackBeforeAddBlackResp{}
|
||||
if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackBeforeAddBlack); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func CallbackAfterAddFriend(ctx context.Context, globalConfig *config.GlobalConfig, req *pbfriend.ApplyToAddFriendReq) error {
|
||||
if !globalConfig.Callback.CallbackAfterAddFriend.Enable {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *friendServer) webhookAfterAddFriend(ctx context.Context, after *config.AfterConfig, req *pbfriend.ApplyToAddFriendReq) {
|
||||
cbReq := &cbapi.CallbackAfterAddFriendReq{
|
||||
CallbackCommand: cbapi.CallbackAfterAddFriendCommand,
|
||||
FromUserID: req.FromUserID,
|
||||
@@ -102,90 +58,100 @@ func CallbackAfterAddFriend(ctx context.Context, globalConfig *config.GlobalConf
|
||||
ReqMsg: req.ReqMsg,
|
||||
}
|
||||
resp := &cbapi.CallbackAfterAddFriendResp{}
|
||||
if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackAfterAddFriend); err != nil {
|
||||
return err
|
||||
}
|
||||
s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, after)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
func CallbackBeforeAddFriendAgree(ctx context.Context, globalConfig *config.GlobalConfig, req *pbfriend.RespondFriendApplyReq) error {
|
||||
if !globalConfig.Callback.CallbackBeforeAddFriendAgree.Enable {
|
||||
return nil
|
||||
}
|
||||
cbReq := &cbapi.CallbackBeforeAddFriendAgreeReq{
|
||||
CallbackCommand: cbapi.CallbackBeforeAddFriendAgreeCommand,
|
||||
FromUserID: req.FromUserID,
|
||||
ToUserID: req.ToUserID,
|
||||
HandleMsg: req.HandleMsg,
|
||||
HandleResult: req.HandleResult,
|
||||
}
|
||||
resp := &cbapi.CallbackBeforeAddFriendAgreeResp{}
|
||||
if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackBeforeAddFriendAgree); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func CallbackAfterDeleteFriend(ctx context.Context, globalConfig *config.GlobalConfig, req *pbfriend.DeleteFriendReq) error {
|
||||
if !globalConfig.Callback.CallbackAfterDeleteFriend.Enable {
|
||||
return nil
|
||||
}
|
||||
cbReq := &cbapi.CallbackAfterDeleteFriendReq{
|
||||
CallbackCommand: cbapi.CallbackAfterDeleteFriendCommand,
|
||||
func (s *friendServer) webhookAfterSetFriendRemark(ctx context.Context, after *config.AfterConfig, req *pbfriend.SetFriendRemarkReq) {
|
||||
|
||||
cbReq := &cbapi.CallbackAfterSetFriendRemarkReq{
|
||||
CallbackCommand: cbapi.CallbackAfterSetFriendRemarkCommand,
|
||||
OwnerUserID: req.OwnerUserID,
|
||||
FriendUserID: req.FriendUserID,
|
||||
Remark: req.Remark,
|
||||
}
|
||||
resp := &cbapi.CallbackAfterDeleteFriendResp{}
|
||||
if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackAfterDeleteFriend); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
resp := &cbapi.CallbackAfterSetFriendRemarkResp{}
|
||||
s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, after)
|
||||
}
|
||||
func CallbackBeforeImportFriends(ctx context.Context, globalConfig *config.GlobalConfig, req *pbfriend.ImportFriendReq) error {
|
||||
if !globalConfig.Callback.CallbackBeforeImportFriends.Enable {
|
||||
return nil
|
||||
}
|
||||
cbReq := &cbapi.CallbackBeforeImportFriendsReq{
|
||||
CallbackCommand: cbapi.CallbackBeforeImportFriendsCommand,
|
||||
OwnerUserID: req.OwnerUserID,
|
||||
FriendUserIDs: req.FriendUserIDs,
|
||||
}
|
||||
resp := &cbapi.CallbackBeforeImportFriendsResp{}
|
||||
if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackBeforeImportFriends); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(resp.FriendUserIDs) != 0 {
|
||||
req.FriendUserIDs = resp.FriendUserIDs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func CallbackAfterImportFriends(ctx context.Context, globalConfig *config.GlobalConfig, req *pbfriend.ImportFriendReq) error {
|
||||
if !globalConfig.Callback.CallbackAfterImportFriends.Enable {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *friendServer) webhookAfterImportFriends(ctx context.Context, after *config.AfterConfig, req *pbfriend.ImportFriendReq) {
|
||||
cbReq := &cbapi.CallbackAfterImportFriendsReq{
|
||||
CallbackCommand: cbapi.CallbackAfterImportFriendsCommand,
|
||||
OwnerUserID: req.OwnerUserID,
|
||||
FriendUserIDs: req.FriendUserIDs,
|
||||
}
|
||||
resp := &cbapi.CallbackAfterImportFriendsResp{}
|
||||
if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackAfterImportFriends); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, after)
|
||||
}
|
||||
|
||||
func CallbackAfterRemoveBlack(ctx context.Context, globalConfig *config.GlobalConfig, req *pbfriend.RemoveBlackReq) error {
|
||||
if !globalConfig.Callback.CallbackAfterRemoveBlack.Enable {
|
||||
return nil
|
||||
}
|
||||
func (s *friendServer) webhookAfterRemoveBlack(ctx context.Context, after *config.AfterConfig, req *pbfriend.RemoveBlackReq) {
|
||||
cbReq := &cbapi.CallbackAfterRemoveBlackReq{
|
||||
CallbackCommand: cbapi.CallbackAfterRemoveBlackCommand,
|
||||
OwnerUserID: req.OwnerUserID,
|
||||
BlackUserID: req.BlackUserID,
|
||||
}
|
||||
resp := &cbapi.CallbackAfterRemoveBlackResp{}
|
||||
if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackAfterRemoveBlack); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, after)
|
||||
}
|
||||
|
||||
func (s *friendServer) webhookBeforeSetFriendRemark(ctx context.Context, before *config.BeforeConfig, req *pbfriend.SetFriendRemarkReq) error {
|
||||
return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
|
||||
cbReq := &cbapi.CallbackBeforeSetFriendRemarkReq{
|
||||
CallbackCommand: cbapi.CallbackBeforeSetFriendRemarkCommand,
|
||||
OwnerUserID: req.OwnerUserID,
|
||||
FriendUserID: req.FriendUserID,
|
||||
Remark: req.Remark,
|
||||
}
|
||||
resp := &cbapi.CallbackBeforeSetFriendRemarkResp{}
|
||||
if err := s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.Remark != "" {
|
||||
req.Remark = resp.Remark
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (s *friendServer) webhookBeforeAddBlack(ctx context.Context, before *config.BeforeConfig, req *pbfriend.AddBlackReq) error {
|
||||
return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
|
||||
cbReq := &cbapi.CallbackBeforeAddBlackReq{
|
||||
CallbackCommand: cbapi.CallbackBeforeAddBlackCommand,
|
||||
OwnerUserID: req.OwnerUserID,
|
||||
BlackUserID: req.BlackUserID,
|
||||
}
|
||||
resp := &cbapi.CallbackBeforeAddBlackResp{}
|
||||
return s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *friendServer) webhookBeforeAddFriendAgree(ctx context.Context, before *config.BeforeConfig, req *pbfriend.RespondFriendApplyReq) error {
|
||||
return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
|
||||
cbReq := &cbapi.CallbackBeforeAddFriendAgreeReq{
|
||||
CallbackCommand: cbapi.CallbackBeforeAddFriendAgreeCommand,
|
||||
FromUserID: req.FromUserID,
|
||||
ToUserID: req.ToUserID,
|
||||
HandleMsg: req.HandleMsg,
|
||||
HandleResult: req.HandleResult,
|
||||
}
|
||||
resp := &cbapi.CallbackBeforeAddFriendAgreeResp{}
|
||||
return s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *friendServer) webhookBeforeImportFriends(ctx context.Context, before *config.BeforeConfig, req *pbfriend.ImportFriendReq) error {
|
||||
return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
|
||||
cbReq := &cbapi.CallbackBeforeImportFriendsReq{
|
||||
CallbackCommand: cbapi.CallbackBeforeImportFriendsCommand,
|
||||
OwnerUserID: req.OwnerUserID,
|
||||
FriendUserIDs: req.FriendUserIDs,
|
||||
}
|
||||
resp := &cbapi.CallbackBeforeImportFriendsResp{}
|
||||
if err := s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(resp.FriendUserIDs) > 0 {
|
||||
req.FriendUserIDs = resp.FriendUserIDs
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
+81
-100
@@ -16,25 +16,25 @@ package friend
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
pbfriend "github.com/OpenIMSDK/protocol/friend"
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
registry "github.com/OpenIMSDK/tools/discoveryregistry"
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/OpenIMSDK/tools/tx"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/webhook"
|
||||
"github.com/openimsdk/tools/db/redisutil"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/convert"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo"
|
||||
tablerelation "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient/notification"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
pbfriend "github.com/openimsdk/protocol/friend"
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"github.com/openimsdk/tools/discovery"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
@@ -42,67 +42,79 @@ type friendServer struct {
|
||||
friendDatabase controller.FriendDatabase
|
||||
blackDatabase controller.BlackDatabase
|
||||
userRpcClient *rpcclient.UserRpcClient
|
||||
notificationSender *notification.FriendNotificationSender
|
||||
notificationSender *FriendNotificationSender
|
||||
conversationRpcClient rpcclient.ConversationRpcClient
|
||||
RegisterCenter registry.SvcDiscoveryRegistry
|
||||
config *config.GlobalConfig
|
||||
RegisterCenter discovery.SvcDiscoveryRegistry
|
||||
config *Config
|
||||
webhookClient *webhook.Client
|
||||
}
|
||||
|
||||
func Start(config *config.GlobalConfig, client registry.SvcDiscoveryRegistry, server *grpc.Server) error {
|
||||
// Initialize MongoDB
|
||||
mongo, err := unrelation.NewMongo(config)
|
||||
type Config struct {
|
||||
RpcConfig config.Friend
|
||||
RedisConfig config.Redis
|
||||
MongodbConfig config.Mongo
|
||||
ZookeeperConfig config.ZooKeeper
|
||||
NotificationConfig config.Notification
|
||||
Share config.Share
|
||||
WebhooksConfig config.Webhooks
|
||||
LocalCacheConfig config.LocalCache
|
||||
}
|
||||
|
||||
func Start(ctx context.Context, config *Config, client discovery.SvcDiscoveryRegistry, server *grpc.Server) error {
|
||||
mgocli, err := mongoutil.NewMongoDB(ctx, config.MongodbConfig.Build())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rdb, err := redisutil.NewRedisClient(ctx, config.RedisConfig.Build())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Initialize Redis
|
||||
rdb, err := cache.NewRedis(config)
|
||||
friendMongoDB, err := mgo.NewFriendMongo(mgocli.GetDB())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
friendMongoDB, err := mgo.NewFriendMongo(mongo.GetDatabase(config.Mongo.Database))
|
||||
friendRequestMongoDB, err := mgo.NewFriendRequestMongo(mgocli.GetDB())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
friendRequestMongoDB, err := mgo.NewFriendRequestMongo(mongo.GetDatabase(config.Mongo.Database))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
blackMongoDB, err := mgo.NewBlackMongo(mongo.GetDatabase(config.Mongo.Database))
|
||||
blackMongoDB, err := mgo.NewBlackMongo(mgocli.GetDB())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Initialize RPC clients
|
||||
userRpcClient := rpcclient.NewUserRpcClient(client, config)
|
||||
msgRpcClient := rpcclient.NewMessageRpcClient(client, config)
|
||||
userRpcClient := rpcclient.NewUserRpcClient(client, config.Share.RpcRegisterName.User, config.Share.IMAdminUserID)
|
||||
msgRpcClient := rpcclient.NewMessageRpcClient(client, config.Share.RpcRegisterName.Msg)
|
||||
|
||||
// Initialize notification sender
|
||||
notificationSender := notification.NewFriendNotificationSender(
|
||||
config,
|
||||
notificationSender := NewFriendNotificationSender(
|
||||
&config.NotificationConfig,
|
||||
&msgRpcClient,
|
||||
notification.WithRpcFunc(userRpcClient.GetUsersInfo),
|
||||
WithRpcFunc(userRpcClient.GetUsersInfo),
|
||||
)
|
||||
cache.InitLocalCache(&config.LocalCacheConfig)
|
||||
|
||||
// Register Friend server with refactored MongoDB and Redis integrations
|
||||
pbfriend.RegisterFriendServer(server, &friendServer{
|
||||
friendDatabase: controller.NewFriendDatabase(
|
||||
friendMongoDB,
|
||||
friendRequestMongoDB,
|
||||
cache.NewFriendCacheRedis(rdb, friendMongoDB, cache.GetDefaultOpt()),
|
||||
tx.NewMongo(mongo.GetClient()),
|
||||
cache.NewFriendCacheRedis(rdb, &config.LocalCacheConfig, friendMongoDB, cache.GetDefaultOpt()),
|
||||
mgocli.GetTx(),
|
||||
),
|
||||
blackDatabase: controller.NewBlackDatabase(
|
||||
blackMongoDB,
|
||||
cache.NewBlackCacheRedis(rdb, blackMongoDB, cache.GetDefaultOpt()),
|
||||
cache.NewBlackCacheRedis(rdb, &config.LocalCacheConfig, blackMongoDB, cache.GetDefaultOpt()),
|
||||
),
|
||||
userRpcClient: &userRpcClient,
|
||||
notificationSender: notificationSender,
|
||||
RegisterCenter: client,
|
||||
conversationRpcClient: rpcclient.NewConversationRpcClient(client, config),
|
||||
conversationRpcClient: rpcclient.NewConversationRpcClient(client, config.Share.RpcRegisterName.Conversation),
|
||||
config: config,
|
||||
webhookClient: webhook.NewWebhookClient(config.WebhooksConfig.URL),
|
||||
})
|
||||
|
||||
return nil
|
||||
@@ -111,18 +123,15 @@ func Start(config *config.GlobalConfig, client registry.SvcDiscoveryRegistry, se
|
||||
// ok.
|
||||
func (s *friendServer) ApplyToAddFriend(ctx context.Context, req *pbfriend.ApplyToAddFriendReq) (resp *pbfriend.ApplyToAddFriendResp, err error) {
|
||||
resp = &pbfriend.ApplyToAddFriendResp{}
|
||||
if err := authverify.CheckAccessV3(ctx, req.FromUserID, s.config); err != nil {
|
||||
if err := authverify.CheckAccessV3(ctx, req.FromUserID, s.config.Share.IMAdminUserID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if req.ToUserID == req.FromUserID {
|
||||
return nil, errs.ErrCanNotAddYourself.Wrap("req.ToUserID", req.ToUserID)
|
||||
return nil, servererrs.ErrCanNotAddYourself.WrapMsg("req.ToUserID", req.ToUserID)
|
||||
}
|
||||
|
||||
if err = CallbackBeforeAddFriend(ctx, s.config, req); err != nil && err != errs.ErrCallbackContinue {
|
||||
if err = s.webhookBeforeAddFriend(ctx, &s.config.WebhooksConfig.BeforeAddFriend, req); err != nil && err != servererrs.ErrCallbackContinue {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := s.userRpcClient.GetUsersInfoMap(ctx, []string{req.ToUserID, req.FromUserID}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -131,41 +140,33 @@ func (s *friendServer) ApplyToAddFriend(ctx context.Context, req *pbfriend.Apply
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if in1 && in2 {
|
||||
return nil, errs.ErrRelationshipAlready.Wrap()
|
||||
return nil, servererrs.ErrRelationshipAlready.WrapMsg("already friends has f")
|
||||
}
|
||||
|
||||
if err = s.friendDatabase.AddFriendRequest(ctx, req.FromUserID, req.ToUserID, req.ReqMsg, req.Ex); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = s.notificationSender.FriendApplicationAddNotification(ctx, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = CallbackAfterAddFriend(ctx, s.config, req); err != nil && err != errs.ErrCallbackContinue {
|
||||
return nil, err
|
||||
}
|
||||
s.notificationSender.FriendApplicationAddNotification(ctx, req)
|
||||
s.webhookAfterAddFriend(ctx, &s.config.WebhooksConfig.AfterAddFriend, req)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// ok.
|
||||
func (s *friendServer) ImportFriends(ctx context.Context, req *pbfriend.ImportFriendReq) (resp *pbfriend.ImportFriendResp, err error) {
|
||||
defer log.ZInfo(ctx, utils.GetFuncName()+" Return")
|
||||
if err := authverify.CheckAdmin(ctx, s.config); err != nil {
|
||||
if err := authverify.CheckAdmin(ctx, s.config.Share.IMAdminUserID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := s.userRpcClient.GetUsersInfo(ctx, append([]string{req.OwnerUserID}, req.FriendUserIDs...)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if utils.Contain(req.OwnerUserID, req.FriendUserIDs...) {
|
||||
return nil, errs.ErrCanNotAddYourself.Wrap()
|
||||
if datautil.Contain(req.OwnerUserID, req.FriendUserIDs...) {
|
||||
return nil, servererrs.ErrCanNotAddYourself.WrapMsg("can not add yourself")
|
||||
}
|
||||
if utils.Duplicate(req.FriendUserIDs) {
|
||||
return nil, errs.ErrArgs.Wrap("friend userID repeated")
|
||||
if datautil.Duplicate(req.FriendUserIDs) {
|
||||
return nil, errs.ErrArgs.WrapMsg("friend userID repeated")
|
||||
}
|
||||
if err := CallbackBeforeImportFriends(ctx, s.config, req); err != nil {
|
||||
|
||||
if err := s.webhookBeforeImportFriends(ctx, &s.config.WebhooksConfig.BeforeImportFriends, req); err != nil && err != servererrs.ErrCallbackContinue {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -179,17 +180,15 @@ func (s *friendServer) ImportFriends(ctx context.Context, req *pbfriend.ImportFr
|
||||
HandleResult: constant.FriendResponseAgree,
|
||||
})
|
||||
}
|
||||
if err := CallbackAfterImportFriends(ctx, s.config, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.webhookAfterImportFriends(ctx, &s.config.WebhooksConfig.AfterImportFriends, req)
|
||||
return &pbfriend.ImportFriendResp{}, nil
|
||||
}
|
||||
|
||||
// ok.
|
||||
func (s *friendServer) RespondFriendApply(ctx context.Context, req *pbfriend.RespondFriendApplyReq) (resp *pbfriend.RespondFriendApplyResp, err error) {
|
||||
defer log.ZInfo(ctx, utils.GetFuncName()+" Return")
|
||||
resp = &pbfriend.RespondFriendApplyResp{}
|
||||
if err := authverify.CheckAccessV3(ctx, req.ToUserID, s.config); err != nil {
|
||||
if err := authverify.CheckAccessV3(ctx, req.ToUserID, s.config.Share.IMAdminUserID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -200,16 +199,14 @@ func (s *friendServer) RespondFriendApply(ctx context.Context, req *pbfriend.Res
|
||||
HandleResult: req.HandleResult,
|
||||
}
|
||||
if req.HandleResult == constant.FriendResponseAgree {
|
||||
if err := CallbackBeforeAddFriendAgree(ctx, s.config, req); err != nil && err != errs.ErrCallbackContinue {
|
||||
if err := s.webhookBeforeAddFriendAgree(ctx, &s.config.WebhooksConfig.BeforeAddFriendAgree, req); err != nil && err != servererrs.ErrCallbackContinue {
|
||||
return nil, err
|
||||
}
|
||||
err := s.friendDatabase.AgreeFriendRequest(ctx, &friendRequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.notificationSender.FriendApplicationAgreedNotification(ctx, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.notificationSender.FriendApplicationAgreedNotification(ctx, req)
|
||||
return resp, nil
|
||||
}
|
||||
if req.HandleResult == constant.FriendResponseRefuse {
|
||||
@@ -220,12 +217,11 @@ func (s *friendServer) RespondFriendApply(ctx context.Context, req *pbfriend.Res
|
||||
s.notificationSender.FriendApplicationRefusedNotification(ctx, req)
|
||||
return resp, nil
|
||||
}
|
||||
return nil, errs.ErrArgs.Wrap("req.HandleResult != -1/1")
|
||||
return nil, errs.ErrArgs.WrapMsg("req.HandleResult != -1/1")
|
||||
}
|
||||
|
||||
// ok.
|
||||
func (s *friendServer) DeleteFriend(ctx context.Context, req *pbfriend.DeleteFriendReq) (resp *pbfriend.DeleteFriendResp, err error) {
|
||||
defer log.ZInfo(ctx, utils.GetFuncName()+" Return")
|
||||
resp = &pbfriend.DeleteFriendResp{}
|
||||
if err := s.userRpcClient.Access(ctx, req.OwnerUserID); err != nil {
|
||||
return nil, err
|
||||
@@ -238,17 +234,13 @@ func (s *friendServer) DeleteFriend(ctx context.Context, req *pbfriend.DeleteFri
|
||||
return nil, err
|
||||
}
|
||||
s.notificationSender.FriendDeletedNotification(ctx, req)
|
||||
if err := CallbackAfterDeleteFriend(ctx, s.config, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.webhookAfterDeleteFriend(ctx, &s.config.WebhooksConfig.AfterDeleteFriend, req)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// ok.
|
||||
func (s *friendServer) SetFriendRemark(ctx context.Context, req *pbfriend.SetFriendRemarkReq) (resp *pbfriend.SetFriendRemarkResp, err error) {
|
||||
defer log.ZInfo(ctx, utils.GetFuncName()+" Return")
|
||||
|
||||
if err = CallbackBeforeSetFriendRemark(ctx, s.config, req); err != nil && err != errs.ErrCallbackContinue {
|
||||
if err = s.webhookBeforeSetFriendRemark(ctx, &s.config.WebhooksConfig.BeforeSetFriendRemark, req); err != nil && err != servererrs.ErrCallbackContinue {
|
||||
return nil, err
|
||||
}
|
||||
resp = &pbfriend.SetFriendRemarkResp{}
|
||||
@@ -262,19 +254,16 @@ func (s *friendServer) SetFriendRemark(ctx context.Context, req *pbfriend.SetFri
|
||||
if err := s.friendDatabase.UpdateRemark(ctx, req.OwnerUserID, req.FriendUserID, req.Remark); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := CallbackAfterSetFriendRemark(ctx, s.config, req); err != nil && err != errs.ErrCallbackContinue {
|
||||
return nil, err
|
||||
}
|
||||
s.webhookAfterSetFriendRemark(ctx, &s.config.WebhooksConfig.AfterSetFriendRemark, req)
|
||||
s.notificationSender.FriendRemarkSetNotification(ctx, req.OwnerUserID, req.FriendUserID)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// ok.
|
||||
func (s *friendServer) GetDesignatedFriends(ctx context.Context, req *pbfriend.GetDesignatedFriendsReq) (resp *pbfriend.GetDesignatedFriendsResp, err error) {
|
||||
defer log.ZInfo(ctx, utils.GetFuncName()+" Return")
|
||||
resp = &pbfriend.GetDesignatedFriendsResp{}
|
||||
if utils.Duplicate(req.FriendUserIDs) {
|
||||
return nil, errs.ErrArgs.Wrap("friend userID repeated")
|
||||
if datautil.Duplicate(req.FriendUserIDs) {
|
||||
return nil, errs.ErrArgs.WrapMsg("friend userID repeated")
|
||||
}
|
||||
friends, err := s.friendDatabase.FindFriendsWithError(ctx, req.OwnerUserID, req.FriendUserIDs)
|
||||
if err != nil {
|
||||
@@ -303,7 +292,6 @@ func (s *friendServer) GetDesignatedFriendsApply(ctx context.Context,
|
||||
|
||||
// Get received friend requests (i.e., those initiated by others).
|
||||
func (s *friendServer) GetPaginationFriendsApplyTo(ctx context.Context, req *pbfriend.GetPaginationFriendsApplyToReq) (resp *pbfriend.GetPaginationFriendsApplyToResp, err error) {
|
||||
defer log.ZInfo(ctx, utils.GetFuncName()+" Return")
|
||||
if err := s.userRpcClient.Access(ctx, req.UserID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -321,7 +309,6 @@ func (s *friendServer) GetPaginationFriendsApplyTo(ctx context.Context, req *pbf
|
||||
}
|
||||
|
||||
func (s *friendServer) GetPaginationFriendsApplyFrom(ctx context.Context, req *pbfriend.GetPaginationFriendsApplyFromReq) (resp *pbfriend.GetPaginationFriendsApplyFromResp, err error) {
|
||||
defer log.ZInfo(ctx, utils.GetFuncName()+" Return")
|
||||
resp = &pbfriend.GetPaginationFriendsApplyFromResp{}
|
||||
if err := s.userRpcClient.Access(ctx, req.UserID); err != nil {
|
||||
return nil, err
|
||||
@@ -340,7 +327,6 @@ func (s *friendServer) GetPaginationFriendsApplyFrom(ctx context.Context, req *p
|
||||
|
||||
// ok.
|
||||
func (s *friendServer) IsFriend(ctx context.Context, req *pbfriend.IsFriendReq) (resp *pbfriend.IsFriendResp, err error) {
|
||||
defer log.ZInfo(ctx, utils.GetFuncName()+" Return")
|
||||
resp = &pbfriend.IsFriendResp{}
|
||||
resp.InUser1Friends, resp.InUser2Friends, err = s.friendDatabase.CheckIn(ctx, req.UserID1, req.UserID2)
|
||||
if err != nil {
|
||||
@@ -350,7 +336,6 @@ func (s *friendServer) IsFriend(ctx context.Context, req *pbfriend.IsFriendReq)
|
||||
}
|
||||
|
||||
func (s *friendServer) GetPaginationFriends(ctx context.Context, req *pbfriend.GetPaginationFriendsReq) (resp *pbfriend.GetPaginationFriendsResp, err error) {
|
||||
defer log.ZInfo(ctx, utils.GetFuncName()+" Return")
|
||||
if err := s.userRpcClient.Access(ctx, req.UserID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -368,7 +353,6 @@ func (s *friendServer) GetPaginationFriends(ctx context.Context, req *pbfriend.G
|
||||
}
|
||||
|
||||
func (s *friendServer) GetFriendIDs(ctx context.Context, req *pbfriend.GetFriendIDsReq) (resp *pbfriend.GetFriendIDsResp, err error) {
|
||||
defer log.ZInfo(ctx, utils.GetFuncName()+" Return")
|
||||
if err := s.userRpcClient.Access(ctx, req.UserID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -382,10 +366,10 @@ func (s *friendServer) GetFriendIDs(ctx context.Context, req *pbfriend.GetFriend
|
||||
|
||||
func (s *friendServer) GetSpecifiedFriendsInfo(ctx context.Context, req *pbfriend.GetSpecifiedFriendsInfoReq) (*pbfriend.GetSpecifiedFriendsInfoResp, error) {
|
||||
if len(req.UserIDList) == 0 {
|
||||
return nil, errs.ErrArgs.Wrap("userIDList is empty")
|
||||
return nil, errs.ErrArgs.WrapMsg("userIDList is empty")
|
||||
}
|
||||
if utils.Duplicate(req.UserIDList) {
|
||||
return nil, errs.ErrArgs.Wrap("userIDList repeated")
|
||||
if datautil.Duplicate(req.UserIDList) {
|
||||
return nil, errs.ErrArgs.WrapMsg("userIDList repeated")
|
||||
}
|
||||
userMap, err := s.userRpcClient.GetUsersInfoMap(ctx, req.UserIDList)
|
||||
if err != nil {
|
||||
@@ -399,10 +383,10 @@ func (s *friendServer) GetSpecifiedFriendsInfo(ctx context.Context, req *pbfrien
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
friendMap := utils.SliceToMap(friends, func(e *tablerelation.FriendModel) string {
|
||||
friendMap := datautil.SliceToMap(friends, func(e *tablerelation.FriendModel) string {
|
||||
return e.FriendUserID
|
||||
})
|
||||
blackMap := utils.SliceToMap(blacks, func(e *tablerelation.BlackModel) string {
|
||||
blackMap := datautil.SliceToMap(blacks, func(e *tablerelation.BlackModel) string {
|
||||
return e.BlockUserID
|
||||
})
|
||||
resp := &pbfriend.GetSpecifiedFriendsInfoResp{
|
||||
@@ -449,10 +433,10 @@ func (s *friendServer) UpdateFriends(
|
||||
req *pbfriend.UpdateFriendsReq,
|
||||
) (*pbfriend.UpdateFriendsResp, error) {
|
||||
if len(req.FriendUserIDs) == 0 {
|
||||
return nil, errs.ErrArgs.Wrap("friendIDList is empty")
|
||||
return nil, errs.ErrArgs.WrapMsg("friendIDList is empty")
|
||||
}
|
||||
if utils.Duplicate(req.FriendUserIDs) {
|
||||
return nil, errs.ErrArgs.Wrap("friendIDList repeated")
|
||||
if datautil.Duplicate(req.FriendUserIDs) {
|
||||
return nil, errs.ErrArgs.WrapMsg("friendIDList repeated")
|
||||
}
|
||||
|
||||
_, err := s.friendDatabase.FindFriendsWithError(ctx, req.OwnerUserID, req.FriendUserIDs)
|
||||
@@ -477,9 +461,6 @@ func (s *friendServer) UpdateFriends(
|
||||
|
||||
resp := &pbfriend.UpdateFriendsResp{}
|
||||
|
||||
err = s.notificationSender.FriendsInfoUpdateNotification(ctx, req.OwnerUserID, req.FriendUserIDs)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err, "FriendsInfoUpdateNotification Error")
|
||||
}
|
||||
s.notificationSender.FriendsInfoUpdateNotification(ctx, req.OwnerUserID, req.FriendUserIDs)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package friend
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/convert"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
|
||||
relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient/notification"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
pbfriend "github.com/openimsdk/protocol/friend"
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
"github.com/openimsdk/tools/mcontext"
|
||||
)
|
||||
|
||||
type FriendNotificationSender struct {
|
||||
*rpcclient.NotificationSender
|
||||
// Target not found err
|
||||
getUsersInfo func(ctx context.Context, userIDs []string) ([]notification.CommonUser, error)
|
||||
// db controller
|
||||
db controller.FriendDatabase
|
||||
}
|
||||
|
||||
type friendNotificationSenderOptions func(*FriendNotificationSender)
|
||||
|
||||
func WithFriendDB(db controller.FriendDatabase) friendNotificationSenderOptions {
|
||||
return func(s *FriendNotificationSender) {
|
||||
s.db = db
|
||||
}
|
||||
}
|
||||
|
||||
func WithDBFunc(
|
||||
fn func(ctx context.Context, userIDs []string) (users []*relationtb.UserModel, err error),
|
||||
) friendNotificationSenderOptions {
|
||||
return func(s *FriendNotificationSender) {
|
||||
f := func(ctx context.Context, userIDs []string) (result []notification.CommonUser, err error) {
|
||||
users, err := fn(ctx, userIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, user := range users {
|
||||
result = append(result, user)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
s.getUsersInfo = f
|
||||
}
|
||||
}
|
||||
|
||||
func WithRpcFunc(
|
||||
fn func(ctx context.Context, userIDs []string) ([]*sdkws.UserInfo, error),
|
||||
) friendNotificationSenderOptions {
|
||||
return func(s *FriendNotificationSender) {
|
||||
f := func(ctx context.Context, userIDs []string) (result []notification.CommonUser, err error) {
|
||||
users, err := fn(ctx, userIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, user := range users {
|
||||
result = append(result, user)
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
s.getUsersInfo = f
|
||||
}
|
||||
}
|
||||
|
||||
func NewFriendNotificationSender(
|
||||
conf *config.Notification,
|
||||
msgRpcClient *rpcclient.MessageRpcClient,
|
||||
opts ...friendNotificationSenderOptions,
|
||||
) *FriendNotificationSender {
|
||||
f := &FriendNotificationSender{
|
||||
NotificationSender: rpcclient.NewNotificationSender(conf, rpcclient.WithRpcClient(msgRpcClient)),
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(f)
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *FriendNotificationSender) getUsersInfoMap(
|
||||
ctx context.Context,
|
||||
userIDs []string,
|
||||
) (map[string]*sdkws.UserInfo, error) {
|
||||
users, err := f.getUsersInfo(ctx, userIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make(map[string]*sdkws.UserInfo)
|
||||
for _, user := range users {
|
||||
result[user.GetUserID()] = user.(*sdkws.UserInfo)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
//nolint:unused
|
||||
func (f *FriendNotificationSender) getFromToUserNickname(
|
||||
ctx context.Context,
|
||||
fromUserID, toUserID string,
|
||||
) (string, string, error) {
|
||||
users, err := f.getUsersInfoMap(ctx, []string{fromUserID, toUserID})
|
||||
if err != nil {
|
||||
return "", "", nil
|
||||
}
|
||||
return users[fromUserID].Nickname, users[toUserID].Nickname, nil
|
||||
}
|
||||
|
||||
func (f *FriendNotificationSender) UserInfoUpdatedNotification(ctx context.Context, changedUserID string) {
|
||||
tips := sdkws.UserInfoUpdatedTips{UserID: changedUserID}
|
||||
f.Notification(ctx, mcontext.GetOpUserID(ctx), changedUserID, constant.UserInfoUpdatedNotification, &tips)
|
||||
}
|
||||
|
||||
func (f *FriendNotificationSender) FriendApplicationAddNotification(ctx context.Context, req *pbfriend.ApplyToAddFriendReq) {
|
||||
tips := sdkws.FriendApplicationTips{FromToUserID: &sdkws.FromToUserID{
|
||||
FromUserID: req.FromUserID,
|
||||
ToUserID: req.ToUserID,
|
||||
}}
|
||||
f.Notification(ctx, req.FromUserID, req.ToUserID, constant.FriendApplicationNotification, &tips)
|
||||
}
|
||||
|
||||
func (f *FriendNotificationSender) FriendApplicationAgreedNotification(
|
||||
ctx context.Context,
|
||||
req *pbfriend.RespondFriendApplyReq,
|
||||
) {
|
||||
tips := sdkws.FriendApplicationApprovedTips{FromToUserID: &sdkws.FromToUserID{
|
||||
FromUserID: req.FromUserID,
|
||||
ToUserID: req.ToUserID,
|
||||
}, HandleMsg: req.HandleMsg}
|
||||
f.Notification(ctx, req.ToUserID, req.FromUserID, constant.FriendApplicationApprovedNotification, &tips)
|
||||
}
|
||||
|
||||
func (f *FriendNotificationSender) FriendApplicationRefusedNotification(
|
||||
ctx context.Context,
|
||||
req *pbfriend.RespondFriendApplyReq,
|
||||
) {
|
||||
tips := sdkws.FriendApplicationApprovedTips{FromToUserID: &sdkws.FromToUserID{
|
||||
FromUserID: req.FromUserID,
|
||||
ToUserID: req.ToUserID,
|
||||
}, HandleMsg: req.HandleMsg}
|
||||
f.Notification(ctx, req.ToUserID, req.FromUserID, constant.FriendApplicationRejectedNotification, &tips)
|
||||
}
|
||||
|
||||
func (f *FriendNotificationSender) FriendAddedNotification(
|
||||
ctx context.Context,
|
||||
operationID, opUserID, fromUserID, toUserID string,
|
||||
) error {
|
||||
tips := sdkws.FriendAddedTips{Friend: &sdkws.FriendInfo{}, OpUser: &sdkws.PublicUserInfo{}}
|
||||
user, err := f.getUsersInfo(ctx, []string{opUserID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tips.OpUser.UserID = user[0].GetUserID()
|
||||
tips.OpUser.Ex = user[0].GetEx()
|
||||
tips.OpUser.Nickname = user[0].GetNickname()
|
||||
tips.OpUser.FaceURL = user[0].GetFaceURL()
|
||||
friends, err := f.db.FindFriendsWithError(ctx, fromUserID, []string{toUserID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tips.Friend, err = convert.FriendDB2Pb(ctx, friends[0], f.getUsersInfoMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.Notification(ctx, fromUserID, toUserID, constant.FriendAddedNotification, &tips)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FriendNotificationSender) FriendDeletedNotification(ctx context.Context, req *pbfriend.DeleteFriendReq) {
|
||||
tips := sdkws.FriendDeletedTips{FromToUserID: &sdkws.FromToUserID{
|
||||
FromUserID: req.OwnerUserID,
|
||||
ToUserID: req.FriendUserID,
|
||||
}}
|
||||
f.Notification(ctx, req.OwnerUserID, req.FriendUserID, constant.FriendDeletedNotification, &tips)
|
||||
}
|
||||
|
||||
func (f *FriendNotificationSender) FriendRemarkSetNotification(ctx context.Context, fromUserID, toUserID string) {
|
||||
tips := sdkws.FriendInfoChangedTips{FromToUserID: &sdkws.FromToUserID{}}
|
||||
tips.FromToUserID.FromUserID = fromUserID
|
||||
tips.FromToUserID.ToUserID = toUserID
|
||||
f.Notification(ctx, fromUserID, toUserID, constant.FriendRemarkSetNotification, &tips)
|
||||
}
|
||||
|
||||
func (f *FriendNotificationSender) FriendsInfoUpdateNotification(ctx context.Context, toUserID string, friendIDs []string) {
|
||||
tips := sdkws.FriendsInfoUpdateTips{FromToUserID: &sdkws.FromToUserID{}}
|
||||
tips.FromToUserID.ToUserID = toUserID
|
||||
tips.FriendIDs = friendIDs
|
||||
f.Notification(ctx, toUserID, toUserID, constant.FriendsInfoUpdateNotification, &tips)
|
||||
}
|
||||
|
||||
func (f *FriendNotificationSender) BlackAddedNotification(ctx context.Context, req *pbfriend.AddBlackReq) {
|
||||
tips := sdkws.BlackAddedTips{FromToUserID: &sdkws.FromToUserID{}}
|
||||
tips.FromToUserID.FromUserID = req.OwnerUserID
|
||||
tips.FromToUserID.ToUserID = req.BlackUserID
|
||||
f.Notification(ctx, req.OwnerUserID, req.BlackUserID, constant.BlackAddedNotification, &tips)
|
||||
}
|
||||
|
||||
func (f *FriendNotificationSender) BlackDeletedNotification(ctx context.Context, req *pbfriend.RemoveBlackReq) {
|
||||
blackDeletedTips := sdkws.BlackDeletedTips{FromToUserID: &sdkws.FromToUserID{
|
||||
FromUserID: req.OwnerUserID,
|
||||
ToUserID: req.BlackUserID,
|
||||
}}
|
||||
f.Notification(ctx, req.OwnerUserID, req.BlackUserID, constant.BlackDeletedNotification, &blackDeletedTips)
|
||||
}
|
||||
|
||||
func (f *FriendNotificationSender) FriendInfoUpdatedNotification(ctx context.Context, changedUserID string, needNotifiedUserID string) {
|
||||
tips := sdkws.UserInfoUpdatedTips{UserID: changedUserID}
|
||||
f.Notification(ctx, mcontext.GetOpUserID(ctx), needNotifiedUserID, constant.FriendInfoUpdatedNotification, &tips)
|
||||
}
|
||||
+10
-10
@@ -17,27 +17,27 @@ package group
|
||||
import (
|
||||
"context"
|
||||
|
||||
pbgroup "github.com/OpenIMSDK/protocol/group"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/convert"
|
||||
pbgroup "github.com/openimsdk/protocol/group"
|
||||
)
|
||||
|
||||
func (s *groupServer) GetGroupInfoCache(
|
||||
ctx context.Context,
|
||||
req *pbgroup.GetGroupInfoCacheReq,
|
||||
) (resp *pbgroup.GetGroupInfoCacheResp, err error) {
|
||||
// GetGroupInfoCache get group info from cache.
|
||||
func (s *groupServer) GetGroupInfoCache(ctx context.Context, req *pbgroup.GetGroupInfoCacheReq) (*pbgroup.GetGroupInfoCacheResp, error) {
|
||||
group, err := s.db.TakeGroup(ctx, req.GroupID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp = &pbgroup.GetGroupInfoCacheResp{GroupInfo: convert.Db2PbGroupInfo(group, "", 0)}
|
||||
return resp, nil
|
||||
return &pbgroup.GetGroupInfoCacheResp{
|
||||
GroupInfo: convert.Db2PbGroupInfo(group, "", 0),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *groupServer) GetGroupMemberCache(ctx context.Context, req *pbgroup.GetGroupMemberCacheReq) (resp *pbgroup.GetGroupMemberCacheResp, err error) {
|
||||
func (s *groupServer) GetGroupMemberCache(ctx context.Context, req *pbgroup.GetGroupMemberCacheReq) (*pbgroup.GetGroupMemberCacheResp, error) {
|
||||
members, err := s.db.TakeGroupMember(ctx, req.GroupID, req.GroupMemberID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp = &pbgroup.GetGroupMemberCacheResp{Member: convert.Db2PbGroupMember(members)}
|
||||
return resp, nil
|
||||
return &pbgroup.GetGroupMemberCacheResp{
|
||||
Member: convert.Db2PbGroupMember(members),
|
||||
}, nil
|
||||
}
|
||||
|
||||
+217
-308
@@ -16,70 +16,67 @@ package group
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/OpenIMSDK/protocol/group"
|
||||
pbgroup "github.com/OpenIMSDK/protocol/group"
|
||||
"github.com/OpenIMSDK/protocol/wrapperspb"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/OpenIMSDK/tools/mcontext"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/apistruct"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/http"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/webhook"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
"github.com/openimsdk/protocol/group"
|
||||
"github.com/openimsdk/protocol/wrapperspb"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/mcontext"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
"time"
|
||||
)
|
||||
|
||||
func CallbackBeforeCreateGroup(ctx context.Context, globalConfig *config.GlobalConfig, req *group.CreateGroupReq) (err error) {
|
||||
if !globalConfig.Callback.CallbackBeforeCreateGroup.Enable {
|
||||
// CallbackBeforeCreateGroup callback before create group.
|
||||
func (s *groupServer) webhookBeforeCreateGroup(ctx context.Context, before *config.BeforeConfig, req *group.CreateGroupReq) error {
|
||||
return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
|
||||
cbReq := &callbackstruct.CallbackBeforeCreateGroupReq{
|
||||
CallbackCommand: callbackstruct.CallbackBeforeCreateGroupCommand,
|
||||
OperationID: mcontext.GetOperationID(ctx),
|
||||
GroupInfo: req.GroupInfo,
|
||||
}
|
||||
cbReq.InitMemberList = append(cbReq.InitMemberList, &apistruct.GroupAddMemberInfo{
|
||||
UserID: req.OwnerUserID,
|
||||
RoleLevel: constant.GroupOwner,
|
||||
})
|
||||
for _, userID := range req.AdminUserIDs {
|
||||
cbReq.InitMemberList = append(cbReq.InitMemberList, &apistruct.GroupAddMemberInfo{
|
||||
UserID: userID,
|
||||
RoleLevel: constant.GroupAdmin,
|
||||
})
|
||||
}
|
||||
for _, userID := range req.MemberUserIDs {
|
||||
cbReq.InitMemberList = append(cbReq.InitMemberList, &apistruct.GroupAddMemberInfo{
|
||||
UserID: userID,
|
||||
RoleLevel: constant.GroupOrdinaryUsers,
|
||||
})
|
||||
}
|
||||
resp := &callbackstruct.CallbackBeforeCreateGroupResp{}
|
||||
|
||||
if err := s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
datautil.NotNilReplace(&req.GroupInfo.GroupID, resp.GroupID)
|
||||
datautil.NotNilReplace(&req.GroupInfo.GroupName, resp.GroupName)
|
||||
datautil.NotNilReplace(&req.GroupInfo.Notification, resp.Notification)
|
||||
datautil.NotNilReplace(&req.GroupInfo.Introduction, resp.Introduction)
|
||||
datautil.NotNilReplace(&req.GroupInfo.FaceURL, resp.FaceURL)
|
||||
datautil.NotNilReplace(&req.GroupInfo.OwnerUserID, resp.OwnerUserID)
|
||||
datautil.NotNilReplace(&req.GroupInfo.Ex, resp.Ex)
|
||||
datautil.NotNilReplace(&req.GroupInfo.Status, resp.Status)
|
||||
datautil.NotNilReplace(&req.GroupInfo.CreatorUserID, resp.CreatorUserID)
|
||||
datautil.NotNilReplace(&req.GroupInfo.GroupType, resp.GroupType)
|
||||
datautil.NotNilReplace(&req.GroupInfo.NeedVerification, resp.NeedVerification)
|
||||
datautil.NotNilReplace(&req.GroupInfo.LookMemberInfo, resp.LookMemberInfo)
|
||||
return nil
|
||||
}
|
||||
cbReq := &callbackstruct.CallbackBeforeCreateGroupReq{
|
||||
CallbackCommand: callbackstruct.CallbackBeforeCreateGroupCommand,
|
||||
OperationID: mcontext.GetOperationID(ctx),
|
||||
GroupInfo: req.GroupInfo,
|
||||
}
|
||||
cbReq.InitMemberList = append(cbReq.InitMemberList, &apistruct.GroupAddMemberInfo{
|
||||
UserID: req.OwnerUserID,
|
||||
RoleLevel: constant.GroupOwner,
|
||||
})
|
||||
for _, userID := range req.AdminUserIDs {
|
||||
cbReq.InitMemberList = append(cbReq.InitMemberList, &apistruct.GroupAddMemberInfo{
|
||||
UserID: userID,
|
||||
RoleLevel: constant.GroupAdmin,
|
||||
})
|
||||
}
|
||||
for _, userID := range req.MemberUserIDs {
|
||||
cbReq.InitMemberList = append(cbReq.InitMemberList, &apistruct.GroupAddMemberInfo{
|
||||
UserID: userID,
|
||||
RoleLevel: constant.GroupOrdinaryUsers,
|
||||
})
|
||||
}
|
||||
resp := &callbackstruct.CallbackBeforeCreateGroupResp{}
|
||||
if err = http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackBeforeCreateGroup); err != nil {
|
||||
return err
|
||||
}
|
||||
utils.NotNilReplace(&req.GroupInfo.GroupID, resp.GroupID)
|
||||
utils.NotNilReplace(&req.GroupInfo.GroupName, resp.GroupName)
|
||||
utils.NotNilReplace(&req.GroupInfo.Notification, resp.Notification)
|
||||
utils.NotNilReplace(&req.GroupInfo.Introduction, resp.Introduction)
|
||||
utils.NotNilReplace(&req.GroupInfo.FaceURL, resp.FaceURL)
|
||||
utils.NotNilReplace(&req.GroupInfo.OwnerUserID, resp.OwnerUserID)
|
||||
utils.NotNilReplace(&req.GroupInfo.Ex, resp.Ex)
|
||||
utils.NotNilReplace(&req.GroupInfo.Status, resp.Status)
|
||||
utils.NotNilReplace(&req.GroupInfo.CreatorUserID, resp.CreatorUserID)
|
||||
utils.NotNilReplace(&req.GroupInfo.GroupType, resp.GroupType)
|
||||
utils.NotNilReplace(&req.GroupInfo.NeedVerification, resp.NeedVerification)
|
||||
utils.NotNilReplace(&req.GroupInfo.LookMemberInfo, resp.LookMemberInfo)
|
||||
return nil
|
||||
}
|
||||
|
||||
func CallbackAfterCreateGroup(ctx context.Context, globalConfig *config.GlobalConfig, req *group.CreateGroupReq) (err error) {
|
||||
if !globalConfig.Callback.CallbackAfterCreateGroup.Enable {
|
||||
return nil
|
||||
}
|
||||
func (s *groupServer) webhookAfterCreateGroup(ctx context.Context, after *config.AfterConfig, req *group.CreateGroupReq) {
|
||||
cbReq := &callbackstruct.CallbackAfterCreateGroupReq{
|
||||
CallbackCommand: callbackstruct.CallbackAfterCreateGroupCommand,
|
||||
GroupInfo: req.GroupInfo,
|
||||
@@ -100,239 +97,163 @@ func CallbackAfterCreateGroup(ctx context.Context, globalConfig *config.GlobalCo
|
||||
RoleLevel: constant.GroupOrdinaryUsers,
|
||||
})
|
||||
}
|
||||
resp := &callbackstruct.CallbackAfterCreateGroupResp{}
|
||||
if err = http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackAfterCreateGroup); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &callbackstruct.CallbackAfterCreateGroupResp{}, after)
|
||||
}
|
||||
|
||||
func CallbackBeforeMemberJoinGroup(
|
||||
ctx context.Context,
|
||||
globalConfig *config.GlobalConfig,
|
||||
groupMember *relation.GroupMemberModel,
|
||||
groupEx string,
|
||||
) (err error) {
|
||||
if !globalConfig.Callback.CallbackBeforeMemberJoinGroup.Enable {
|
||||
func (s *groupServer) webhookBeforeMemberJoinGroup(ctx context.Context, before *config.BeforeConfig, groupMember *relation.GroupMemberModel, groupEx string) error {
|
||||
return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
|
||||
cbReq := &callbackstruct.CallbackBeforeMemberJoinGroupReq{
|
||||
CallbackCommand: callbackstruct.CallbackBeforeMemberJoinGroupCommand,
|
||||
GroupID: groupMember.GroupID,
|
||||
UserID: groupMember.UserID,
|
||||
Ex: groupMember.Ex,
|
||||
GroupEx: groupEx,
|
||||
}
|
||||
resp := &callbackstruct.CallbackBeforeMemberJoinGroupResp{}
|
||||
if err := s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if resp.MuteEndTime != nil {
|
||||
groupMember.MuteEndTime = time.UnixMilli(*resp.MuteEndTime)
|
||||
}
|
||||
datautil.NotNilReplace(&groupMember.FaceURL, resp.FaceURL)
|
||||
datautil.NotNilReplace(&groupMember.Ex, resp.Ex)
|
||||
datautil.NotNilReplace(&groupMember.Nickname, resp.Nickname)
|
||||
datautil.NotNilReplace(&groupMember.RoleLevel, resp.RoleLevel)
|
||||
return nil
|
||||
}
|
||||
callbackReq := &callbackstruct.CallbackBeforeMemberJoinGroupReq{
|
||||
CallbackCommand: callbackstruct.CallbackBeforeMemberJoinGroupCommand,
|
||||
GroupID: groupMember.GroupID,
|
||||
UserID: groupMember.UserID,
|
||||
Ex: groupMember.Ex,
|
||||
GroupEx: groupEx,
|
||||
}
|
||||
resp := &callbackstruct.CallbackBeforeMemberJoinGroupResp{}
|
||||
err = http.CallBackPostReturn(
|
||||
ctx,
|
||||
globalConfig.Callback.CallbackUrl,
|
||||
callbackReq,
|
||||
resp,
|
||||
globalConfig.Callback.CallbackBeforeMemberJoinGroup,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.MuteEndTime != nil {
|
||||
groupMember.MuteEndTime = time.UnixMilli(*resp.MuteEndTime)
|
||||
}
|
||||
utils.NotNilReplace(&groupMember.FaceURL, resp.FaceURL)
|
||||
utils.NotNilReplace(&groupMember.Ex, resp.Ex)
|
||||
utils.NotNilReplace(&groupMember.Nickname, resp.Nickname)
|
||||
utils.NotNilReplace(&groupMember.RoleLevel, resp.RoleLevel)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func CallbackBeforeSetGroupMemberInfo(ctx context.Context, globalConfig *config.GlobalConfig, req *group.SetGroupMemberInfo) (err error) {
|
||||
if !globalConfig.Callback.CallbackBeforeSetGroupMemberInfo.Enable {
|
||||
func (s *groupServer) webhookBeforeSetGroupMemberInfo(ctx context.Context, before *config.BeforeConfig, req *group.SetGroupMemberInfo) error {
|
||||
return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
|
||||
cbReq := callbackstruct.CallbackBeforeSetGroupMemberInfoReq{
|
||||
CallbackCommand: callbackstruct.CallbackBeforeSetGroupMemberInfoCommand,
|
||||
GroupID: req.GroupID,
|
||||
UserID: req.UserID,
|
||||
}
|
||||
if req.Nickname != nil {
|
||||
cbReq.Nickname = &req.Nickname.Value
|
||||
}
|
||||
if req.FaceURL != nil {
|
||||
cbReq.FaceURL = &req.FaceURL.Value
|
||||
}
|
||||
if req.RoleLevel != nil {
|
||||
cbReq.RoleLevel = &req.RoleLevel.Value
|
||||
}
|
||||
if req.Ex != nil {
|
||||
cbReq.Ex = &req.Ex.Value
|
||||
}
|
||||
resp := &callbackstruct.CallbackBeforeSetGroupMemberInfoResp{}
|
||||
if err := s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.FaceURL != nil {
|
||||
req.FaceURL = wrapperspb.String(*resp.FaceURL)
|
||||
}
|
||||
if resp.Nickname != nil {
|
||||
req.Nickname = wrapperspb.String(*resp.Nickname)
|
||||
}
|
||||
if resp.RoleLevel != nil {
|
||||
req.RoleLevel = wrapperspb.Int32(*resp.RoleLevel)
|
||||
}
|
||||
if resp.Ex != nil {
|
||||
req.Ex = wrapperspb.String(*resp.Ex)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
callbackReq := callbackstruct.CallbackBeforeSetGroupMemberInfoReq{
|
||||
CallbackCommand: callbackstruct.CallbackBeforeSetGroupMemberInfoCommand,
|
||||
GroupID: req.GroupID,
|
||||
UserID: req.UserID,
|
||||
}
|
||||
if req.Nickname != nil {
|
||||
callbackReq.Nickname = &req.Nickname.Value
|
||||
}
|
||||
if req.FaceURL != nil {
|
||||
callbackReq.FaceURL = &req.FaceURL.Value
|
||||
}
|
||||
if req.RoleLevel != nil {
|
||||
callbackReq.RoleLevel = &req.RoleLevel.Value
|
||||
}
|
||||
if req.Ex != nil {
|
||||
callbackReq.Ex = &req.Ex.Value
|
||||
}
|
||||
resp := &callbackstruct.CallbackBeforeSetGroupMemberInfoResp{}
|
||||
err = http.CallBackPostReturn(
|
||||
ctx,
|
||||
globalConfig.Callback.CallbackUrl,
|
||||
callbackReq,
|
||||
resp,
|
||||
globalConfig.Callback.CallbackBeforeSetGroupMemberInfo,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.FaceURL != nil {
|
||||
req.FaceURL = wrapperspb.String(*resp.FaceURL)
|
||||
}
|
||||
if resp.Nickname != nil {
|
||||
req.Nickname = wrapperspb.String(*resp.Nickname)
|
||||
}
|
||||
if resp.RoleLevel != nil {
|
||||
req.RoleLevel = wrapperspb.Int32(*resp.RoleLevel)
|
||||
}
|
||||
if resp.Ex != nil {
|
||||
req.Ex = wrapperspb.String(*resp.Ex)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
func CallbackAfterSetGroupMemberInfo(ctx context.Context, globalConfig *config.GlobalConfig, req *group.SetGroupMemberInfo) (err error) {
|
||||
if !globalConfig.Callback.CallbackBeforeSetGroupMemberInfo.Enable {
|
||||
return nil
|
||||
}
|
||||
callbackReq := callbackstruct.CallbackAfterSetGroupMemberInfoReq{
|
||||
|
||||
func (s *groupServer) webhookAfterSetGroupMemberInfo(ctx context.Context, after *config.AfterConfig, req *group.SetGroupMemberInfo) {
|
||||
cbReq := callbackstruct.CallbackAfterSetGroupMemberInfoReq{
|
||||
CallbackCommand: callbackstruct.CallbackAfterSetGroupMemberInfoCommand,
|
||||
GroupID: req.GroupID,
|
||||
UserID: req.UserID,
|
||||
}
|
||||
if req.Nickname != nil {
|
||||
callbackReq.Nickname = &req.Nickname.Value
|
||||
cbReq.Nickname = &req.Nickname.Value
|
||||
}
|
||||
if req.FaceURL != nil {
|
||||
callbackReq.FaceURL = &req.FaceURL.Value
|
||||
cbReq.FaceURL = &req.FaceURL.Value
|
||||
}
|
||||
if req.RoleLevel != nil {
|
||||
callbackReq.RoleLevel = &req.RoleLevel.Value
|
||||
cbReq.RoleLevel = &req.RoleLevel.Value
|
||||
}
|
||||
if req.Ex != nil {
|
||||
callbackReq.Ex = &req.Ex.Value
|
||||
cbReq.Ex = &req.Ex.Value
|
||||
}
|
||||
resp := &callbackstruct.CallbackAfterSetGroupMemberInfoResp{}
|
||||
if err = http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, callbackReq, resp, globalConfig.Callback.CallbackAfterSetGroupMemberInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &callbackstruct.CallbackAfterSetGroupMemberInfoResp{}, after)
|
||||
}
|
||||
|
||||
func CallbackQuitGroup(ctx context.Context, globalConfig *config.GlobalConfig, req *group.QuitGroupReq) (err error) {
|
||||
if !globalConfig.Callback.CallbackQuitGroup.Enable {
|
||||
return nil
|
||||
}
|
||||
func (s *groupServer) webhookAfterQuitGroup(ctx context.Context, after *config.AfterConfig, req *group.QuitGroupReq) {
|
||||
cbReq := &callbackstruct.CallbackQuitGroupReq{
|
||||
CallbackCommand: callbackstruct.CallbackQuitGroupCommand,
|
||||
CallbackCommand: callbackstruct.CallbackAfterQuitGroupCommand,
|
||||
GroupID: req.GroupID,
|
||||
UserID: req.UserID,
|
||||
}
|
||||
resp := &callbackstruct.CallbackQuitGroupResp{}
|
||||
if err = http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackQuitGroup); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &callbackstruct.CallbackQuitGroupResp{}, after)
|
||||
}
|
||||
|
||||
func CallbackKillGroupMember(ctx context.Context, globalConfig *config.GlobalConfig, req *pbgroup.KickGroupMemberReq) (err error) {
|
||||
if !globalConfig.Callback.CallbackKillGroupMember.Enable {
|
||||
return nil
|
||||
}
|
||||
func (s *groupServer) webhookAfterKickGroupMember(ctx context.Context, after *config.AfterConfig, req *group.KickGroupMemberReq) {
|
||||
cbReq := &callbackstruct.CallbackKillGroupMemberReq{
|
||||
CallbackCommand: callbackstruct.CallbackKillGroupCommand,
|
||||
CallbackCommand: callbackstruct.CallbackAfterKickGroupCommand,
|
||||
GroupID: req.GroupID,
|
||||
KickedUserIDs: req.KickedUserIDs,
|
||||
}
|
||||
resp := &callbackstruct.CallbackKillGroupMemberResp{}
|
||||
if err = http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackQuitGroup); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &callbackstruct.CallbackKillGroupMemberResp{}, after)
|
||||
}
|
||||
|
||||
func CallbackDismissGroup(ctx context.Context, globalConfig *config.GlobalConfig, req *callbackstruct.CallbackDisMissGroupReq) (err error) {
|
||||
if !globalConfig.Callback.CallbackDismissGroup.Enable {
|
||||
return nil
|
||||
}
|
||||
req.CallbackCommand = callbackstruct.CallbackDisMissGroupCommand
|
||||
resp := &callbackstruct.CallbackDisMissGroupResp{}
|
||||
if err = http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackQuitGroup); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
func (s *groupServer) webhookAfterDismissGroup(ctx context.Context, after *config.AfterConfig, req *callbackstruct.CallbackDisMissGroupReq) {
|
||||
req.CallbackCommand = callbackstruct.CallbackAfterDisMissGroupCommand
|
||||
s.webhookClient.AsyncPost(ctx, req.GetCallbackCommand(), req, &callbackstruct.CallbackDisMissGroupResp{}, after)
|
||||
}
|
||||
|
||||
func CallbackApplyJoinGroupBefore(ctx context.Context, globalConfig *config.GlobalConfig, req *callbackstruct.CallbackJoinGroupReq) (err error) {
|
||||
if !globalConfig.Callback.CallbackBeforeJoinGroup.Enable {
|
||||
func (s *groupServer) webhookBeforeApplyJoinGroup(ctx context.Context, before *config.BeforeConfig, req *callbackstruct.CallbackJoinGroupReq) (err error) {
|
||||
return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
|
||||
req.CallbackCommand = callbackstruct.CallbackBeforeJoinGroupCommand
|
||||
resp := &callbackstruct.CallbackJoinGroupResp{}
|
||||
if err := s.webhookClient.SyncPost(ctx, req.GetCallbackCommand(), req, resp, before); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
req.CallbackCommand = callbackstruct.CallbackBeforeJoinGroupCommand
|
||||
|
||||
resp := &callbackstruct.CallbackJoinGroupResp{}
|
||||
if err = http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackBeforeJoinGroup); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func CallbackAfterTransferGroupOwner(ctx context.Context, globalConfig *config.GlobalConfig, req *pbgroup.TransferGroupOwnerReq) (err error) {
|
||||
if !globalConfig.Callback.CallbackAfterTransferGroupOwner.Enable {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *groupServer) webhookAfterTransferGroupOwner(ctx context.Context, after *config.AfterConfig, req *group.TransferGroupOwnerReq) {
|
||||
cbReq := &callbackstruct.CallbackTransferGroupOwnerReq{
|
||||
CallbackCommand: callbackstruct.CallbackAfterTransferGroupOwner,
|
||||
CallbackCommand: callbackstruct.CallbackAfterTransferGroupOwnerCommand,
|
||||
GroupID: req.GroupID,
|
||||
OldOwnerUserID: req.OldOwnerUserID,
|
||||
NewOwnerUserID: req.NewOwnerUserID,
|
||||
}
|
||||
|
||||
resp := &callbackstruct.CallbackTransferGroupOwnerResp{}
|
||||
if err = http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackAfterTransferGroupOwner); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func CallbackBeforeInviteUserToGroup(ctx context.Context, globalConfig *config.GlobalConfig, req *group.InviteUserToGroupReq) (err error) {
|
||||
if !globalConfig.Callback.CallbackBeforeInviteUserToGroup.Enable {
|
||||
return nil
|
||||
}
|
||||
|
||||
callbackReq := &callbackstruct.CallbackBeforeInviteUserToGroupReq{
|
||||
CallbackCommand: callbackstruct.CallbackBeforeInviteJoinGroupCommand,
|
||||
OperationID: mcontext.GetOperationID(ctx),
|
||||
GroupID: req.GroupID,
|
||||
Reason: req.Reason,
|
||||
InvitedUserIDs: req.InvitedUserIDs,
|
||||
}
|
||||
|
||||
resp := &callbackstruct.CallbackBeforeInviteUserToGroupResp{}
|
||||
err = http.CallBackPostReturn(
|
||||
ctx,
|
||||
globalConfig.Callback.CallbackUrl,
|
||||
callbackReq,
|
||||
resp,
|
||||
globalConfig.Callback.CallbackBeforeInviteUserToGroup,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(resp.RefusedMembersAccount) > 0 {
|
||||
// Handle the scenario where certain members are refused
|
||||
// You might want to update the req.Members list or handle it as per your business logic
|
||||
}
|
||||
return nil
|
||||
s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &callbackstruct.CallbackTransferGroupOwnerResp{}, after)
|
||||
}
|
||||
|
||||
func CallbackAfterJoinGroup(ctx context.Context, globalConfig *config.GlobalConfig, req *group.JoinGroupReq) error {
|
||||
if !globalConfig.Callback.CallbackAfterJoinGroup.Enable {
|
||||
func (s *groupServer) webhookBeforeInviteUserToGroup(ctx context.Context, before *config.BeforeConfig, req *group.InviteUserToGroupReq) (err error) {
|
||||
return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
|
||||
cbReq := &callbackstruct.CallbackBeforeInviteUserToGroupReq{
|
||||
CallbackCommand: callbackstruct.CallbackBeforeInviteJoinGroupCommand,
|
||||
OperationID: mcontext.GetOperationID(ctx),
|
||||
GroupID: req.GroupID,
|
||||
Reason: req.Reason,
|
||||
InvitedUserIDs: req.InvitedUserIDs,
|
||||
}
|
||||
|
||||
resp := &callbackstruct.CallbackBeforeInviteUserToGroupResp{}
|
||||
if err := s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(resp.RefusedMembersAccount) > 0 {
|
||||
// Handle the scenario where certain members are refused
|
||||
// You might want to update the req.Members list or handle it as per your business logic
|
||||
}
|
||||
return nil
|
||||
}
|
||||
callbackReq := &callbackstruct.CallbackAfterJoinGroupReq{
|
||||
})
|
||||
}
|
||||
|
||||
func (s *groupServer) webhookAfterJoinGroup(ctx context.Context, after *config.AfterConfig, req *group.JoinGroupReq) {
|
||||
cbReq := &callbackstruct.CallbackAfterJoinGroupReq{
|
||||
CallbackCommand: callbackstruct.CallbackAfterJoinGroupCommand,
|
||||
OperationID: mcontext.GetOperationID(ctx),
|
||||
GroupID: req.GroupID,
|
||||
@@ -340,68 +261,60 @@ func CallbackAfterJoinGroup(ctx context.Context, globalConfig *config.GlobalConf
|
||||
JoinSource: req.JoinSource,
|
||||
InviterUserID: req.InviterUserID,
|
||||
}
|
||||
resp := &callbackstruct.CallbackAfterJoinGroupResp{}
|
||||
if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, callbackReq, resp, globalConfig.Callback.CallbackAfterJoinGroup); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &callbackstruct.CallbackAfterJoinGroupResp{}, after)
|
||||
}
|
||||
|
||||
func CallbackBeforeSetGroupInfo(ctx context.Context, globalConfig *config.GlobalConfig, req *group.SetGroupInfoReq) error {
|
||||
if !globalConfig.Callback.CallbackBeforeSetGroupInfo.Enable {
|
||||
func (s *groupServer) webhookBeforeSetGroupInfo(ctx context.Context, before *config.BeforeConfig, req *group.SetGroupInfoReq) error {
|
||||
return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
|
||||
cbReq := &callbackstruct.CallbackBeforeSetGroupInfoReq{
|
||||
CallbackCommand: callbackstruct.CallbackBeforeSetGroupInfoCommand,
|
||||
GroupID: req.GroupInfoForSet.GroupID,
|
||||
Notification: req.GroupInfoForSet.Notification,
|
||||
Introduction: req.GroupInfoForSet.Introduction,
|
||||
FaceURL: req.GroupInfoForSet.FaceURL,
|
||||
GroupName: req.GroupInfoForSet.GroupName,
|
||||
}
|
||||
if req.GroupInfoForSet.Ex != nil {
|
||||
cbReq.Ex = req.GroupInfoForSet.Ex.Value
|
||||
}
|
||||
log.ZDebug(ctx, "debug CallbackBeforeSetGroupInfo", "ex", cbReq.Ex)
|
||||
if req.GroupInfoForSet.NeedVerification != nil {
|
||||
cbReq.NeedVerification = req.GroupInfoForSet.NeedVerification.Value
|
||||
}
|
||||
if req.GroupInfoForSet.LookMemberInfo != nil {
|
||||
cbReq.LookMemberInfo = req.GroupInfoForSet.LookMemberInfo.Value
|
||||
}
|
||||
if req.GroupInfoForSet.ApplyMemberFriend != nil {
|
||||
cbReq.ApplyMemberFriend = req.GroupInfoForSet.ApplyMemberFriend.Value
|
||||
}
|
||||
resp := &callbackstruct.CallbackBeforeSetGroupInfoResp{}
|
||||
|
||||
if err := s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if resp.Ex != nil {
|
||||
req.GroupInfoForSet.Ex = wrapperspb.String(*resp.Ex)
|
||||
}
|
||||
if resp.NeedVerification != nil {
|
||||
req.GroupInfoForSet.NeedVerification = wrapperspb.Int32(*resp.NeedVerification)
|
||||
}
|
||||
if resp.LookMemberInfo != nil {
|
||||
req.GroupInfoForSet.LookMemberInfo = wrapperspb.Int32(*resp.LookMemberInfo)
|
||||
}
|
||||
if resp.ApplyMemberFriend != nil {
|
||||
req.GroupInfoForSet.ApplyMemberFriend = wrapperspb.Int32(*resp.ApplyMemberFriend)
|
||||
}
|
||||
datautil.NotNilReplace(&req.GroupInfoForSet.GroupID, &resp.GroupID)
|
||||
datautil.NotNilReplace(&req.GroupInfoForSet.GroupName, &resp.GroupName)
|
||||
datautil.NotNilReplace(&req.GroupInfoForSet.FaceURL, &resp.FaceURL)
|
||||
datautil.NotNilReplace(&req.GroupInfoForSet.Introduction, &resp.Introduction)
|
||||
return nil
|
||||
}
|
||||
callbackReq := &callbackstruct.CallbackBeforeSetGroupInfoReq{
|
||||
CallbackCommand: callbackstruct.CallbackBeforeSetGroupInfoCommand,
|
||||
GroupID: req.GroupInfoForSet.GroupID,
|
||||
Notification: req.GroupInfoForSet.Notification,
|
||||
Introduction: req.GroupInfoForSet.Introduction,
|
||||
FaceURL: req.GroupInfoForSet.FaceURL,
|
||||
GroupName: req.GroupInfoForSet.GroupName,
|
||||
}
|
||||
|
||||
if req.GroupInfoForSet.Ex != nil {
|
||||
callbackReq.Ex = req.GroupInfoForSet.Ex.Value
|
||||
}
|
||||
log.ZDebug(ctx, "debug CallbackBeforeSetGroupInfo", callbackReq.Ex)
|
||||
if req.GroupInfoForSet.NeedVerification != nil {
|
||||
callbackReq.NeedVerification = req.GroupInfoForSet.NeedVerification.Value
|
||||
}
|
||||
if req.GroupInfoForSet.LookMemberInfo != nil {
|
||||
callbackReq.LookMemberInfo = req.GroupInfoForSet.LookMemberInfo.Value
|
||||
}
|
||||
if req.GroupInfoForSet.ApplyMemberFriend != nil {
|
||||
callbackReq.ApplyMemberFriend = req.GroupInfoForSet.ApplyMemberFriend.Value
|
||||
}
|
||||
resp := &callbackstruct.CallbackBeforeSetGroupInfoResp{}
|
||||
|
||||
if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, callbackReq, resp, globalConfig.Callback.CallbackBeforeSetGroupInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if resp.Ex != nil {
|
||||
req.GroupInfoForSet.Ex = wrapperspb.String(*resp.Ex)
|
||||
}
|
||||
if resp.NeedVerification != nil {
|
||||
req.GroupInfoForSet.NeedVerification = wrapperspb.Int32(*resp.NeedVerification)
|
||||
}
|
||||
if resp.LookMemberInfo != nil {
|
||||
req.GroupInfoForSet.LookMemberInfo = wrapperspb.Int32(*resp.LookMemberInfo)
|
||||
}
|
||||
if resp.ApplyMemberFriend != nil {
|
||||
req.GroupInfoForSet.ApplyMemberFriend = wrapperspb.Int32(*resp.ApplyMemberFriend)
|
||||
}
|
||||
utils.NotNilReplace(&req.GroupInfoForSet.GroupID, &resp.GroupID)
|
||||
utils.NotNilReplace(&req.GroupInfoForSet.GroupName, &resp.GroupName)
|
||||
utils.NotNilReplace(&req.GroupInfoForSet.FaceURL, &resp.FaceURL)
|
||||
utils.NotNilReplace(&req.GroupInfoForSet.Introduction, &resp.Introduction)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
func CallbackAfterSetGroupInfo(ctx context.Context, globalConfig *config.GlobalConfig, req *group.SetGroupInfoReq) error {
|
||||
if !globalConfig.Callback.CallbackAfterSetGroupInfo.Enable {
|
||||
return nil
|
||||
}
|
||||
callbackReq := &callbackstruct.CallbackAfterSetGroupInfoReq{
|
||||
|
||||
func (s *groupServer) webhookAfterSetGroupInfo(ctx context.Context, after *config.AfterConfig, req *group.SetGroupInfoReq) {
|
||||
cbReq := &callbackstruct.CallbackAfterSetGroupInfoReq{
|
||||
CallbackCommand: callbackstruct.CallbackAfterSetGroupInfoCommand,
|
||||
GroupID: req.GroupInfoForSet.GroupID,
|
||||
Notification: req.GroupInfoForSet.Notification,
|
||||
@@ -410,20 +323,16 @@ func CallbackAfterSetGroupInfo(ctx context.Context, globalConfig *config.GlobalC
|
||||
GroupName: req.GroupInfoForSet.GroupName,
|
||||
}
|
||||
if req.GroupInfoForSet.Ex != nil {
|
||||
callbackReq.Ex = &req.GroupInfoForSet.Ex.Value
|
||||
cbReq.Ex = &req.GroupInfoForSet.Ex.Value
|
||||
}
|
||||
if req.GroupInfoForSet.NeedVerification != nil {
|
||||
callbackReq.NeedVerification = &req.GroupInfoForSet.NeedVerification.Value
|
||||
cbReq.NeedVerification = &req.GroupInfoForSet.NeedVerification.Value
|
||||
}
|
||||
if req.GroupInfoForSet.LookMemberInfo != nil {
|
||||
callbackReq.LookMemberInfo = &req.GroupInfoForSet.LookMemberInfo.Value
|
||||
cbReq.LookMemberInfo = &req.GroupInfoForSet.LookMemberInfo.Value
|
||||
}
|
||||
if req.GroupInfoForSet.ApplyMemberFriend != nil {
|
||||
callbackReq.ApplyMemberFriend = &req.GroupInfoForSet.ApplyMemberFriend.Value
|
||||
cbReq.ApplyMemberFriend = &req.GroupInfoForSet.ApplyMemberFriend.Value
|
||||
}
|
||||
resp := &callbackstruct.CallbackAfterSetGroupInfoResp{}
|
||||
if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, callbackReq, resp, globalConfig.Callback.CallbackAfterSetGroupInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &callbackstruct.CallbackAfterSetGroupInfoResp{}, after)
|
||||
}
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
package group
|
||||
|
||||
import (
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
)
|
||||
|
||||
func (s *groupServer) groupDB2PB(group *relation.GroupModel, ownerUserID string, memberCount uint32) *sdkws.GroupInfo {
|
||||
@@ -41,10 +41,7 @@ func (s *groupServer) groupDB2PB(group *relation.GroupModel, ownerUserID string,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *groupServer) groupMemberDB2PB(
|
||||
member *relation.GroupMemberModel,
|
||||
appMangerLevel int32,
|
||||
) *sdkws.GroupMemberFullInfo {
|
||||
func (s *groupServer) groupMemberDB2PB(member *relation.GroupMemberModel, appMangerLevel int32) *sdkws.GroupMemberFullInfo {
|
||||
return &sdkws.GroupMemberFullInfo{
|
||||
GroupID: member.GroupID,
|
||||
UserID: member.UserID,
|
||||
|
||||
@@ -18,9 +18,9 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
pbgroup "github.com/OpenIMSDK/protocol/group"
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
"github.com/OpenIMSDK/tools/mcontext"
|
||||
pbgroup "github.com/openimsdk/protocol/group"
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
"github.com/openimsdk/tools/mcontext"
|
||||
)
|
||||
|
||||
func UpdateGroupInfoMap(ctx context.Context, group *sdkws.GroupInfoForSet) map[string]any {
|
||||
|
||||
@@ -21,5 +21,5 @@ import (
|
||||
)
|
||||
|
||||
func (s *groupServer) PopulateGroupMember(ctx context.Context, members ...*relationtb.GroupMemberModel) error {
|
||||
return s.Notification.PopulateGroupMember(ctx, members...)
|
||||
return s.notification.PopulateGroupMember(ctx, members...)
|
||||
}
|
||||
|
||||
+341
-335
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,721 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package group
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient/notification"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
pbgroup "github.com/openimsdk/protocol/group"
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/mcontext"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
"github.com/openimsdk/tools/utils/stringutil"
|
||||
)
|
||||
|
||||
func NewGroupNotificationSender(db controller.GroupDatabase, msgRpcClient *rpcclient.MessageRpcClient, userRpcClient *rpcclient.UserRpcClient, config *Config, fn func(ctx context.Context, userIDs []string) ([]notification.CommonUser, error)) *GroupNotificationSender {
|
||||
return &GroupNotificationSender{
|
||||
NotificationSender: rpcclient.NewNotificationSender(&config.NotificationConfig, rpcclient.WithRpcClient(msgRpcClient), rpcclient.WithUserRpcClient(userRpcClient)),
|
||||
getUsersInfo: fn,
|
||||
db: db,
|
||||
config: config,
|
||||
}
|
||||
}
|
||||
|
||||
type GroupNotificationSender struct {
|
||||
*rpcclient.NotificationSender
|
||||
getUsersInfo func(ctx context.Context, userIDs []string) ([]notification.CommonUser, error)
|
||||
db controller.GroupDatabase
|
||||
config *Config
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) PopulateGroupMember(ctx context.Context, members ...*relation.GroupMemberModel) error {
|
||||
if len(members) == 0 {
|
||||
return nil
|
||||
}
|
||||
emptyUserIDs := make(map[string]struct{})
|
||||
for _, member := range members {
|
||||
if member.Nickname == "" || member.FaceURL == "" {
|
||||
emptyUserIDs[member.UserID] = struct{}{}
|
||||
}
|
||||
}
|
||||
if len(emptyUserIDs) > 0 {
|
||||
users, err := g.getUsersInfo(ctx, datautil.Keys(emptyUserIDs))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
userMap := make(map[string]notification.CommonUser)
|
||||
for i, user := range users {
|
||||
userMap[user.GetUserID()] = users[i]
|
||||
}
|
||||
for i, member := range members {
|
||||
user, ok := userMap[member.UserID]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if member.Nickname == "" {
|
||||
members[i].Nickname = user.GetNickname()
|
||||
}
|
||||
if member.FaceURL == "" {
|
||||
members[i].FaceURL = user.GetFaceURL()
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) getUser(ctx context.Context, userID string) (*sdkws.PublicUserInfo, error) {
|
||||
users, err := g.getUsersInfo(ctx, []string{userID})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(users) == 0 {
|
||||
return nil, servererrs.ErrUserIDNotFound.WrapMsg(fmt.Sprintf("user %s not found", userID))
|
||||
}
|
||||
return &sdkws.PublicUserInfo{
|
||||
UserID: users[0].GetUserID(),
|
||||
Nickname: users[0].GetNickname(),
|
||||
FaceURL: users[0].GetFaceURL(),
|
||||
Ex: users[0].GetEx(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) getGroupInfo(ctx context.Context, groupID string) (*sdkws.GroupInfo, error) {
|
||||
gm, err := g.db.TakeGroup(ctx, groupID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
num, err := g.db.FindGroupMemberNum(ctx, groupID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ownerUserIDs, err := g.db.GetGroupRoleLevelMemberIDs(ctx, groupID, constant.GroupOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var ownerUserID string
|
||||
if len(ownerUserIDs) > 0 {
|
||||
ownerUserID = ownerUserIDs[0]
|
||||
}
|
||||
return &sdkws.GroupInfo{
|
||||
GroupID: gm.GroupID,
|
||||
GroupName: gm.GroupName,
|
||||
Notification: gm.Notification,
|
||||
Introduction: gm.Introduction,
|
||||
FaceURL: gm.FaceURL,
|
||||
OwnerUserID: ownerUserID,
|
||||
CreateTime: gm.CreateTime.UnixMilli(),
|
||||
MemberCount: num,
|
||||
Ex: gm.Ex,
|
||||
Status: gm.Status,
|
||||
CreatorUserID: gm.CreatorUserID,
|
||||
GroupType: gm.GroupType,
|
||||
NeedVerification: gm.NeedVerification,
|
||||
LookMemberInfo: gm.LookMemberInfo,
|
||||
ApplyMemberFriend: gm.ApplyMemberFriend,
|
||||
NotificationUpdateTime: gm.NotificationUpdateTime.UnixMilli(),
|
||||
NotificationUserID: gm.NotificationUserID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) getGroupMembers(ctx context.Context, groupID string, userIDs []string) ([]*sdkws.GroupMemberFullInfo, error) {
|
||||
members, err := g.db.FindGroupMembers(ctx, groupID, userIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := g.PopulateGroupMember(ctx, members...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.ZDebug(ctx, "getGroupMembers", "members", members)
|
||||
res := make([]*sdkws.GroupMemberFullInfo, 0, len(members))
|
||||
for _, member := range members {
|
||||
res = append(res, g.groupMemberDB2PB(member, 0))
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) getGroupMemberMap(ctx context.Context, groupID string, userIDs []string) (map[string]*sdkws.GroupMemberFullInfo, error) {
|
||||
members, err := g.getGroupMembers(ctx, groupID, userIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m := make(map[string]*sdkws.GroupMemberFullInfo)
|
||||
for i, member := range members {
|
||||
m[member.UserID] = members[i]
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) getGroupMember(ctx context.Context, groupID string, userID string) (*sdkws.GroupMemberFullInfo, error) {
|
||||
members, err := g.getGroupMembers(ctx, groupID, []string{userID})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(members) == 0 {
|
||||
return nil, errs.ErrInternalServer.WrapMsg(fmt.Sprintf("group %s member %s not found", groupID, userID))
|
||||
}
|
||||
return members[0], nil
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) getGroupOwnerAndAdminUserID(ctx context.Context, groupID string) ([]string, error) {
|
||||
members, err := g.db.FindGroupMemberRoleLevels(ctx, groupID, []int32{constant.GroupOwner, constant.GroupAdmin})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := g.PopulateGroupMember(ctx, members...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fn := func(e *relation.GroupMemberModel) string { return e.UserID }
|
||||
return datautil.Slice(members, fn), nil
|
||||
}
|
||||
|
||||
//nolint:unused
|
||||
func (g *GroupNotificationSender) groupDB2PB(group *relation.GroupModel, ownerUserID string, memberCount uint32) *sdkws.GroupInfo {
|
||||
return &sdkws.GroupInfo{
|
||||
GroupID: group.GroupID,
|
||||
GroupName: group.GroupName,
|
||||
Notification: group.Notification,
|
||||
Introduction: group.Introduction,
|
||||
FaceURL: group.FaceURL,
|
||||
OwnerUserID: ownerUserID,
|
||||
CreateTime: group.CreateTime.UnixMilli(),
|
||||
MemberCount: memberCount,
|
||||
Ex: group.Ex,
|
||||
Status: group.Status,
|
||||
CreatorUserID: group.CreatorUserID,
|
||||
GroupType: group.GroupType,
|
||||
NeedVerification: group.NeedVerification,
|
||||
LookMemberInfo: group.LookMemberInfo,
|
||||
ApplyMemberFriend: group.ApplyMemberFriend,
|
||||
NotificationUpdateTime: group.NotificationUpdateTime.UnixMilli(),
|
||||
NotificationUserID: group.NotificationUserID,
|
||||
}
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) groupMemberDB2PB(member *relation.GroupMemberModel, appMangerLevel int32) *sdkws.GroupMemberFullInfo {
|
||||
return &sdkws.GroupMemberFullInfo{
|
||||
GroupID: member.GroupID,
|
||||
UserID: member.UserID,
|
||||
RoleLevel: member.RoleLevel,
|
||||
JoinTime: member.JoinTime.UnixMilli(),
|
||||
Nickname: member.Nickname,
|
||||
FaceURL: member.FaceURL,
|
||||
AppMangerLevel: appMangerLevel,
|
||||
JoinSource: member.JoinSource,
|
||||
OperatorUserID: member.OperatorUserID,
|
||||
Ex: member.Ex,
|
||||
MuteEndTime: member.MuteEndTime.UnixMilli(),
|
||||
InviterUserID: member.InviterUserID,
|
||||
}
|
||||
}
|
||||
|
||||
/* func (g *GroupNotificationSender) getUsersInfoMap(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error) {
|
||||
users, err := g.getUsersInfo(ctx, userIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make(map[string]*sdkws.UserInfo)
|
||||
for _, user := range users {
|
||||
result[user.GetUserID()] = user.(*sdkws.UserInfo)
|
||||
}
|
||||
return result, nil
|
||||
} */
|
||||
|
||||
func (g *GroupNotificationSender) fillOpUser(ctx context.Context, opUser **sdkws.GroupMemberFullInfo, groupID string) (err error) {
|
||||
if opUser == nil {
|
||||
return errs.ErrInternalServer.WrapMsg("**sdkws.GroupMemberFullInfo is nil")
|
||||
}
|
||||
userID := mcontext.GetOpUserID(ctx)
|
||||
if groupID != "" {
|
||||
if authverify.IsManagerUserID(userID, g.config.Share.IMAdminUserID) {
|
||||
*opUser = &sdkws.GroupMemberFullInfo{
|
||||
GroupID: groupID,
|
||||
UserID: userID,
|
||||
RoleLevel: constant.GroupAdmin,
|
||||
AppMangerLevel: constant.AppAdmin,
|
||||
}
|
||||
} else {
|
||||
member, err := g.db.TakeGroupMember(ctx, groupID, userID)
|
||||
if err == nil {
|
||||
*opUser = g.groupMemberDB2PB(member, 0)
|
||||
} else if !errs.ErrRecordNotFound.Is(err) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
user, err := g.getUser(ctx, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if *opUser == nil {
|
||||
*opUser = &sdkws.GroupMemberFullInfo{
|
||||
GroupID: groupID,
|
||||
UserID: userID,
|
||||
Nickname: user.Nickname,
|
||||
FaceURL: user.FaceURL,
|
||||
OperatorUserID: userID,
|
||||
}
|
||||
} else {
|
||||
if (*opUser).Nickname == "" {
|
||||
(*opUser).Nickname = user.Nickname
|
||||
}
|
||||
if (*opUser).FaceURL == "" {
|
||||
(*opUser).FaceURL = user.FaceURL
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) GroupCreatedNotification(ctx context.Context, tips *sdkws.GroupCreatedTips) {
|
||||
var err error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
|
||||
return
|
||||
}
|
||||
g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.GroupCreatedNotification, tips)
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) GroupInfoSetNotification(ctx context.Context, tips *sdkws.GroupInfoSetTips) {
|
||||
var err error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
|
||||
return
|
||||
}
|
||||
g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.GroupInfoSetNotification, tips, rpcclient.WithRpcGetUserName())
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) GroupInfoSetNameNotification(ctx context.Context, tips *sdkws.GroupInfoSetNameTips) {
|
||||
var err error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
|
||||
return
|
||||
}
|
||||
g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.GroupInfoSetNameNotification, tips)
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) GroupInfoSetAnnouncementNotification(ctx context.Context, tips *sdkws.GroupInfoSetAnnouncementTips) {
|
||||
var err error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
|
||||
return
|
||||
}
|
||||
g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.GroupInfoSetAnnouncementNotification, tips, rpcclient.WithRpcGetUserName())
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) JoinGroupApplicationNotification(ctx context.Context, req *pbgroup.JoinGroupReq) {
|
||||
var err error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
var group *sdkws.GroupInfo
|
||||
group, err = g.getGroupInfo(ctx, req.GroupID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var user *sdkws.PublicUserInfo
|
||||
user, err = g.getUser(ctx, req.InviterUserID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
userIDs, err := g.getGroupOwnerAndAdminUserID(ctx, req.GroupID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
userIDs = append(userIDs, req.InviterUserID, mcontext.GetOpUserID(ctx))
|
||||
tips := &sdkws.JoinGroupApplicationTips{Group: group, Applicant: user, ReqMsg: req.ReqMessage}
|
||||
for _, userID := range datautil.Distinct(userIDs) {
|
||||
g.Notification(ctx, mcontext.GetOpUserID(ctx), userID, constant.JoinGroupApplicationNotification, tips)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) MemberQuitNotification(ctx context.Context, member *sdkws.GroupMemberFullInfo) {
|
||||
var err error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
var group *sdkws.GroupInfo
|
||||
group, err = g.getGroupInfo(ctx, member.GroupID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tips := &sdkws.MemberQuitTips{Group: group, QuitUser: member}
|
||||
g.Notification(ctx, mcontext.GetOpUserID(ctx), member.GroupID, constant.MemberQuitNotification, tips)
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) GroupApplicationAcceptedNotification(ctx context.Context, req *pbgroup.GroupApplicationResponseReq) {
|
||||
var err error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
var group *sdkws.GroupInfo
|
||||
group, err = g.getGroupInfo(ctx, req.GroupID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var userIDs []string
|
||||
userIDs, err = g.getGroupOwnerAndAdminUserID(ctx, req.GroupID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tips := &sdkws.GroupApplicationAcceptedTips{Group: group, HandleMsg: req.HandledMsg}
|
||||
if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
|
||||
return
|
||||
}
|
||||
for _, userID := range append(userIDs, req.FromUserID) {
|
||||
if userID == req.FromUserID {
|
||||
tips.ReceiverAs = 0
|
||||
} else {
|
||||
tips.ReceiverAs = 1
|
||||
}
|
||||
g.Notification(ctx, mcontext.GetOpUserID(ctx), userID, constant.GroupApplicationAcceptedNotification, tips)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) GroupApplicationRejectedNotification(ctx context.Context, req *pbgroup.GroupApplicationResponseReq) {
|
||||
var err error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
var group *sdkws.GroupInfo
|
||||
group, err = g.getGroupInfo(ctx, req.GroupID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var userIDs []string
|
||||
userIDs, err = g.getGroupOwnerAndAdminUserID(ctx, req.GroupID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tips := &sdkws.GroupApplicationRejectedTips{Group: group, HandleMsg: req.HandledMsg}
|
||||
if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
|
||||
return
|
||||
}
|
||||
for _, userID := range append(userIDs, req.FromUserID) {
|
||||
if userID == req.FromUserID {
|
||||
tips.ReceiverAs = 0
|
||||
} else {
|
||||
tips.ReceiverAs = 1
|
||||
}
|
||||
g.Notification(ctx, mcontext.GetOpUserID(ctx), userID, constant.GroupApplicationRejectedNotification, tips)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) GroupOwnerTransferredNotification(ctx context.Context, req *pbgroup.TransferGroupOwnerReq) {
|
||||
var err error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
var group *sdkws.GroupInfo
|
||||
group, err = g.getGroupInfo(ctx, req.GroupID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
opUserID := mcontext.GetOpUserID(ctx)
|
||||
var member map[string]*sdkws.GroupMemberFullInfo
|
||||
member, err = g.getGroupMemberMap(ctx, req.GroupID, []string{opUserID, req.NewOwnerUserID})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tips := &sdkws.GroupOwnerTransferredTips{Group: group, OpUser: member[opUserID], NewGroupOwner: member[req.NewOwnerUserID]}
|
||||
if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
|
||||
return
|
||||
}
|
||||
g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupOwnerTransferredNotification, tips)
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) MemberKickedNotification(ctx context.Context, tips *sdkws.MemberKickedTips) {
|
||||
var err error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
|
||||
return
|
||||
}
|
||||
g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.MemberKickedNotification, tips)
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) MemberInvitedNotification(ctx context.Context, groupID, reason string, invitedUserIDList []string) {
|
||||
var err error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
var group *sdkws.GroupInfo
|
||||
group, err = g.getGroupInfo(ctx, groupID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var users []*sdkws.GroupMemberFullInfo
|
||||
users, err = g.getGroupMembers(ctx, groupID, invitedUserIDList)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tips := &sdkws.MemberInvitedTips{Group: group, InvitedUserList: users}
|
||||
err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID)
|
||||
g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.MemberInvitedNotification, tips)
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) MemberEnterNotification(ctx context.Context, groupID string, entrantUserID string) {
|
||||
var err error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
var group *sdkws.GroupInfo
|
||||
group, err = g.getGroupInfo(ctx, groupID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var user *sdkws.GroupMemberFullInfo
|
||||
user, err = g.getGroupMember(ctx, groupID, entrantUserID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tips := &sdkws.MemberEnterTips{Group: group, EntrantUser: user}
|
||||
g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.MemberEnterNotification, tips)
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) GroupDismissedNotification(ctx context.Context, tips *sdkws.GroupDismissedTips) {
|
||||
var err error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
|
||||
return
|
||||
}
|
||||
g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.GroupDismissedNotification, tips)
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) GroupMemberMutedNotification(ctx context.Context, groupID, groupMemberUserID string, mutedSeconds uint32) {
|
||||
var err error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
var group *sdkws.GroupInfo
|
||||
group, err = g.getGroupInfo(ctx, groupID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var user map[string]*sdkws.GroupMemberFullInfo
|
||||
user, err = g.getGroupMemberMap(ctx, groupID, []string{mcontext.GetOpUserID(ctx), groupMemberUserID})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tips := &sdkws.GroupMemberMutedTips{
|
||||
Group: group, MutedSeconds: mutedSeconds,
|
||||
OpUser: user[mcontext.GetOpUserID(ctx)], MutedUser: user[groupMemberUserID],
|
||||
}
|
||||
if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
|
||||
return
|
||||
}
|
||||
g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupMemberMutedNotification, tips)
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) GroupMemberCancelMutedNotification(ctx context.Context, groupID, groupMemberUserID string) {
|
||||
var err error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
var group *sdkws.GroupInfo
|
||||
group, err = g.getGroupInfo(ctx, groupID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var user map[string]*sdkws.GroupMemberFullInfo
|
||||
user, err = g.getGroupMemberMap(ctx, groupID, []string{mcontext.GetOpUserID(ctx), groupMemberUserID})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tips := &sdkws.GroupMemberCancelMutedTips{Group: group, OpUser: user[mcontext.GetOpUserID(ctx)], MutedUser: user[groupMemberUserID]}
|
||||
if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
|
||||
return
|
||||
}
|
||||
g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupMemberCancelMutedNotification, tips)
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) GroupMutedNotification(ctx context.Context, groupID string) {
|
||||
var err error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
var group *sdkws.GroupInfo
|
||||
group, err = g.getGroupInfo(ctx, groupID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var users []*sdkws.GroupMemberFullInfo
|
||||
users, err = g.getGroupMembers(ctx, groupID, []string{mcontext.GetOpUserID(ctx)})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tips := &sdkws.GroupMutedTips{Group: group}
|
||||
if len(users) > 0 {
|
||||
tips.OpUser = users[0]
|
||||
}
|
||||
if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
|
||||
return
|
||||
}
|
||||
g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupMutedNotification, tips)
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) GroupCancelMutedNotification(ctx context.Context, groupID string) {
|
||||
var err error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
var group *sdkws.GroupInfo
|
||||
group, err = g.getGroupInfo(ctx, groupID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var users []*sdkws.GroupMemberFullInfo
|
||||
users, err = g.getGroupMembers(ctx, groupID, []string{mcontext.GetOpUserID(ctx)})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tips := &sdkws.GroupCancelMutedTips{Group: group}
|
||||
if len(users) > 0 {
|
||||
tips.OpUser = users[0]
|
||||
}
|
||||
if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
|
||||
return
|
||||
}
|
||||
g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupCancelMutedNotification, tips)
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) GroupMemberInfoSetNotification(ctx context.Context, groupID, groupMemberUserID string) {
|
||||
var err error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
var group *sdkws.GroupInfo
|
||||
group, err = g.getGroupInfo(ctx, groupID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var user map[string]*sdkws.GroupMemberFullInfo
|
||||
user, err = g.getGroupMemberMap(ctx, groupID, []string{groupMemberUserID})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tips := &sdkws.GroupMemberInfoSetTips{Group: group, OpUser: user[mcontext.GetOpUserID(ctx)], ChangedUser: user[groupMemberUserID]}
|
||||
if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
|
||||
return
|
||||
}
|
||||
g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupMemberInfoSetNotification, tips)
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) GroupMemberSetToAdminNotification(ctx context.Context, groupID, groupMemberUserID string) {
|
||||
var err error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
var group *sdkws.GroupInfo
|
||||
group, err = g.getGroupInfo(ctx, groupID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
user, err := g.getGroupMemberMap(ctx, groupID, []string{mcontext.GetOpUserID(ctx), groupMemberUserID})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tips := &sdkws.GroupMemberInfoSetTips{Group: group, OpUser: user[mcontext.GetOpUserID(ctx)], ChangedUser: user[groupMemberUserID]}
|
||||
if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
|
||||
return
|
||||
}
|
||||
g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupMemberSetToAdminNotification, tips)
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) GroupMemberSetToOrdinaryUserNotification(ctx context.Context, groupID, groupMemberUserID string) {
|
||||
var err error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
var group *sdkws.GroupInfo
|
||||
group, err = g.getGroupInfo(ctx, groupID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var user map[string]*sdkws.GroupMemberFullInfo
|
||||
user, err = g.getGroupMemberMap(ctx, groupID, []string{mcontext.GetOpUserID(ctx), groupMemberUserID})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tips := &sdkws.GroupMemberInfoSetTips{Group: group, OpUser: user[mcontext.GetOpUserID(ctx)], ChangedUser: user[groupMemberUserID]}
|
||||
if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
|
||||
return
|
||||
}
|
||||
g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupMemberSetToOrdinaryUserNotification, tips)
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) SuperGroupNotification(ctx context.Context, sendID, recvID string) {
|
||||
g.Notification(ctx, sendID, recvID, constant.SuperGroupUpdateNotification, nil)
|
||||
}
|
||||
@@ -18,13 +18,13 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/group"
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/openimsdk/protocol/group"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
)
|
||||
|
||||
func (s *groupServer) GroupCreateCount(ctx context.Context, req *group.GroupCreateCountReq) (*group.GroupCreateCountResp, error) {
|
||||
if req.Start > req.End {
|
||||
return nil, errs.ErrArgs.Wrap("start > end")
|
||||
return nil, errs.ErrArgs.WrapMsg("start > end: %d > %d", req.Start, req.End)
|
||||
}
|
||||
total, err := s.db.CountTotal(ctx, nil)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package group
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
pbgroup "github.com/OpenIMSDK/protocol/group"
|
||||
)
|
||||
|
||||
func (s *groupServer) GetJoinedSuperGroupList(context.Context, *pbgroup.GetJoinedSuperGroupListReq) (*pbgroup.GetJoinedSuperGroupListResp, error) {
|
||||
return nil, errors.New("deprecated")
|
||||
}
|
||||
|
||||
func (s *groupServer) GetSuperGroupsInfo(context.Context, *pbgroup.GetSuperGroupsInfoReq) (resp *pbgroup.GetSuperGroupsInfoResp, err error) {
|
||||
return nil, errors.New("deprecated")
|
||||
}
|
||||
+47
-80
@@ -17,19 +17,20 @@ package msg
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/OpenIMSDK/protocol/msg"
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
utils2 "github.com/OpenIMSDK/tools/utils"
|
||||
cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
"github.com/openimsdk/protocol/msg"
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
func (m *msgServer) GetConversationsHasReadAndMaxSeq(ctx context.Context, req *msg.GetConversationsHasReadAndMaxSeqReq) (resp *msg.GetConversationsHasReadAndMaxSeqResp, err error) {
|
||||
func (m *msgServer) GetConversationsHasReadAndMaxSeq(ctx context.Context, req *msg.GetConversationsHasReadAndMaxSeqReq) (*msg.GetConversationsHasReadAndMaxSeqResp, error) {
|
||||
var conversationIDs []string
|
||||
if len(req.ConversationIDs) == 0 {
|
||||
var err error
|
||||
conversationIDs, err = m.ConversationLocalCache.GetConversationIDs(ctx, req.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -37,14 +38,17 @@ func (m *msgServer) GetConversationsHasReadAndMaxSeq(ctx context.Context, req *m
|
||||
} else {
|
||||
conversationIDs = req.ConversationIDs
|
||||
}
|
||||
|
||||
hasReadSeqs, err := m.MsgDatabase.GetHasReadSeqs(ctx, req.UserID, conversationIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conversations, err := m.ConversationLocalCache.GetConversations(ctx, req.UserID, conversationIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conversationMaxSeqMap := make(map[string]int64)
|
||||
for _, conversation := range conversations {
|
||||
if conversation.MaxSeq != 0 {
|
||||
@@ -55,95 +59,77 @@ func (m *msgServer) GetConversationsHasReadAndMaxSeq(ctx context.Context, req *m
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp = &msg.GetConversationsHasReadAndMaxSeqResp{Seqs: make(map[string]*msg.Seqs)}
|
||||
for conversarionID, maxSeq := range maxSeqs {
|
||||
resp.Seqs[conversarionID] = &msg.Seqs{
|
||||
HasReadSeq: hasReadSeqs[conversarionID],
|
||||
resp := &msg.GetConversationsHasReadAndMaxSeqResp{Seqs: make(map[string]*msg.Seqs)}
|
||||
for conversationID, maxSeq := range maxSeqs {
|
||||
resp.Seqs[conversationID] = &msg.Seqs{
|
||||
HasReadSeq: hasReadSeqs[conversationID],
|
||||
MaxSeq: maxSeq,
|
||||
}
|
||||
if v, ok := conversationMaxSeqMap[conversarionID]; ok {
|
||||
resp.Seqs[conversarionID].MaxSeq = v
|
||||
if v, ok := conversationMaxSeqMap[conversationID]; ok {
|
||||
resp.Seqs[conversationID].MaxSeq = v
|
||||
}
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (m *msgServer) SetConversationHasReadSeq(
|
||||
ctx context.Context,
|
||||
req *msg.SetConversationHasReadSeqReq,
|
||||
) (resp *msg.SetConversationHasReadSeqResp, err error) {
|
||||
func (m *msgServer) SetConversationHasReadSeq(ctx context.Context, req *msg.SetConversationHasReadSeqReq) (*msg.SetConversationHasReadSeqResp, error) {
|
||||
maxSeq, err := m.MsgDatabase.GetMaxSeq(ctx, req.ConversationID)
|
||||
if err != nil {
|
||||
return
|
||||
return nil, err
|
||||
}
|
||||
if req.HasReadSeq > maxSeq {
|
||||
return nil, errs.ErrArgs.Wrap("hasReadSeq must not be bigger than maxSeq")
|
||||
return nil, errs.ErrArgs.WrapMsg("hasReadSeq must not be bigger than maxSeq")
|
||||
}
|
||||
if err := m.MsgDatabase.SetHasReadSeq(ctx, req.UserID, req.ConversationID, req.HasReadSeq); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = m.sendMarkAsReadNotification(ctx, req.ConversationID, constant.SingleChatType, req.UserID,
|
||||
req.UserID, nil, req.HasReadSeq); err != nil {
|
||||
return
|
||||
}
|
||||
m.sendMarkAsReadNotification(ctx, req.ConversationID, constant.SingleChatType, req.UserID, req.UserID, nil, req.HasReadSeq)
|
||||
return &msg.SetConversationHasReadSeqResp{}, nil
|
||||
}
|
||||
|
||||
func (m *msgServer) MarkMsgsAsRead(
|
||||
ctx context.Context,
|
||||
req *msg.MarkMsgsAsReadReq,
|
||||
) (resp *msg.MarkMsgsAsReadResp, err error) {
|
||||
func (m *msgServer) MarkMsgsAsRead(ctx context.Context, req *msg.MarkMsgsAsReadReq) (*msg.MarkMsgsAsReadResp, error) {
|
||||
if len(req.Seqs) < 1 {
|
||||
return nil, errs.ErrArgs.Wrap("seqs must not be empty")
|
||||
return nil, errs.ErrArgs.WrapMsg("seqs must not be empty")
|
||||
}
|
||||
maxSeq, err := m.MsgDatabase.GetMaxSeq(ctx, req.ConversationID)
|
||||
if err != nil {
|
||||
return
|
||||
return nil, err
|
||||
}
|
||||
hasReadSeq := req.Seqs[len(req.Seqs)-1]
|
||||
if hasReadSeq > maxSeq {
|
||||
return nil, errs.ErrArgs.Wrap("hasReadSeq must not be bigger than maxSeq")
|
||||
return nil, errs.ErrArgs.WrapMsg("hasReadSeq must not be bigger than maxSeq")
|
||||
}
|
||||
conversation, err := m.ConversationLocalCache.GetConversation(ctx, req.UserID, req.ConversationID)
|
||||
if err != nil {
|
||||
return
|
||||
return nil, err
|
||||
}
|
||||
if err = m.MsgDatabase.MarkSingleChatMsgsAsRead(ctx, req.UserID, req.ConversationID, req.Seqs); err != nil {
|
||||
return
|
||||
if err := m.MsgDatabase.MarkSingleChatMsgsAsRead(ctx, req.UserID, req.ConversationID, req.Seqs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
currentHasReadSeq, err := m.MsgDatabase.GetHasReadSeq(ctx, req.UserID, req.ConversationID)
|
||||
if err != nil && errs.Unwrap(err) != redis.Nil {
|
||||
return
|
||||
return nil, err
|
||||
}
|
||||
if hasReadSeq > currentHasReadSeq {
|
||||
err = m.MsgDatabase.SetHasReadSeq(ctx, req.UserID, req.ConversationID, hasReadSeq)
|
||||
if err != nil {
|
||||
return
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
req_callback := &cbapi.CallbackSingleMsgReadReq{
|
||||
reqCallback := &cbapi.CallbackSingleMsgReadReq{
|
||||
ConversationID: conversation.ConversationID,
|
||||
UserID: req.UserID,
|
||||
Seqs: req.Seqs,
|
||||
ContentType: conversation.ConversationType,
|
||||
}
|
||||
if err = CallbackSingleMsgRead(ctx, m.config, req_callback); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = m.sendMarkAsReadNotification(ctx, req.ConversationID, conversation.ConversationType, req.UserID,
|
||||
m.conversationAndGetRecvID(conversation, req.UserID), req.Seqs, hasReadSeq); err != nil {
|
||||
return
|
||||
}
|
||||
m.webhookAfterSingleMsgRead(ctx, &m.config.WebhooksConfig.AfterSingleMsgRead, reqCallback)
|
||||
m.sendMarkAsReadNotification(ctx, req.ConversationID, conversation.ConversationType, req.UserID,
|
||||
m.conversationAndGetRecvID(conversation, req.UserID), req.Seqs, hasReadSeq)
|
||||
return &msg.MarkMsgsAsReadResp{}, nil
|
||||
}
|
||||
|
||||
func (m *msgServer) MarkConversationAsRead(
|
||||
ctx context.Context,
|
||||
req *msg.MarkConversationAsReadReq,
|
||||
) (resp *msg.MarkConversationAsReadResp, err error) {
|
||||
func (m *msgServer) MarkConversationAsRead(ctx context.Context, req *msg.MarkConversationAsReadReq) (*msg.MarkConversationAsReadResp, error) {
|
||||
conversation, err := m.ConversationLocalCache.GetConversation(ctx, req.UserID, req.ConversationID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -154,15 +140,14 @@ func (m *msgServer) MarkConversationAsRead(
|
||||
}
|
||||
var seqs []int64
|
||||
|
||||
log.ZDebug(ctx, "MarkConversationAsRead", "hasReadSeq", hasReadSeq,
|
||||
"req.HasReadSeq", req.HasReadSeq)
|
||||
log.ZDebug(ctx, "MarkConversationAsRead", "hasReadSeq", hasReadSeq, "req.HasReadSeq", req.HasReadSeq)
|
||||
if conversation.ConversationType == constant.SingleChatType {
|
||||
for i := hasReadSeq + 1; i <= req.HasReadSeq; i++ {
|
||||
seqs = append(seqs, i)
|
||||
}
|
||||
//avoid client missed call MarkConversationMessageAsRead by order
|
||||
// avoid client missed call MarkConversationMessageAsRead by order
|
||||
for _, val := range req.Seqs {
|
||||
if !utils2.Contain(val, seqs...) {
|
||||
if !datautil.Contain(val, seqs...) {
|
||||
seqs = append(seqs, val)
|
||||
}
|
||||
}
|
||||
@@ -179,12 +164,9 @@ func (m *msgServer) MarkConversationAsRead(
|
||||
}
|
||||
hasReadSeq = req.HasReadSeq
|
||||
}
|
||||
if err = m.sendMarkAsReadNotification(ctx, req.ConversationID, conversation.ConversationType, req.UserID,
|
||||
m.conversationAndGetRecvID(conversation, req.UserID), seqs, hasReadSeq); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
} else if conversation.ConversationType == constant.SuperGroupChatType ||
|
||||
m.sendMarkAsReadNotification(ctx, req.ConversationID, conversation.ConversationType, req.UserID,
|
||||
m.conversationAndGetRecvID(conversation, req.UserID), seqs, hasReadSeq)
|
||||
} else if conversation.ConversationType == constant.ReadGroupChatType ||
|
||||
conversation.ConversationType == constant.NotificationChatType {
|
||||
if req.HasReadSeq > hasReadSeq {
|
||||
err = m.MsgDatabase.SetHasReadSeq(ctx, req.UserID, req.ConversationID, req.HasReadSeq)
|
||||
@@ -193,11 +175,8 @@ func (m *msgServer) MarkConversationAsRead(
|
||||
}
|
||||
hasReadSeq = req.HasReadSeq
|
||||
}
|
||||
if err = m.sendMarkAsReadNotification(ctx, req.ConversationID, constant.SingleChatType, req.UserID,
|
||||
req.UserID, seqs, hasReadSeq); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m.sendMarkAsReadNotification(ctx, req.ConversationID, constant.SingleChatType, req.UserID,
|
||||
req.UserID, seqs, hasReadSeq)
|
||||
}
|
||||
|
||||
reqCall := &cbapi.CallbackGroupMsgReadReq{
|
||||
@@ -206,30 +185,18 @@ func (m *msgServer) MarkConversationAsRead(
|
||||
UnreadMsgNum: req.HasReadSeq,
|
||||
ContentType: int64(conversation.ConversationType),
|
||||
}
|
||||
if err := CallbackGroupMsgRead(ctx, m.config, reqCall); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m.webhookAfterGroupMsgRead(ctx, &m.config.WebhooksConfig.AfterGroupMsgRead, reqCall)
|
||||
return &msg.MarkConversationAsReadResp{}, nil
|
||||
}
|
||||
|
||||
func (m *msgServer) sendMarkAsReadNotification(
|
||||
ctx context.Context,
|
||||
conversationID string,
|
||||
sessionType int32,
|
||||
sendID, recvID string,
|
||||
seqs []int64,
|
||||
hasReadSeq int64,
|
||||
) error {
|
||||
func (m *msgServer) sendMarkAsReadNotification(ctx context.Context, conversationID string, sessionType int32, sendID, recvID string, seqs []int64, hasReadSeq int64) {
|
||||
tips := &sdkws.MarkAsReadTips{
|
||||
MarkAsReadUserID: sendID,
|
||||
ConversationID: conversationID,
|
||||
Seqs: seqs,
|
||||
HasReadSeq: hasReadSeq,
|
||||
}
|
||||
err := m.notificationSender.NotificationWithSesstionType(ctx, sendID, recvID, constant.HasReadReceipt, sessionType, tips)
|
||||
if err != nil {
|
||||
log.ZWarn(ctx, "send has read Receipt err", err)
|
||||
}
|
||||
return nil
|
||||
m.notificationSender.NotificationWithSessionType(ctx, sendID, recvID, constant.HasReadReceipt, sessionType, tips)
|
||||
|
||||
}
|
||||
|
||||
+92
-113
@@ -16,16 +16,15 @@ package msg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/webhook"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
pbchat "github.com/OpenIMSDK/protocol/msg"
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/OpenIMSDK/tools/mcontext"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/http"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
pbchat "github.com/openimsdk/protocol/msg"
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
"github.com/openimsdk/tools/mcontext"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
@@ -62,138 +61,118 @@ func GetContent(msg *sdkws.MsgData) string {
|
||||
}
|
||||
}
|
||||
|
||||
func callbackBeforeSendSingleMsg(ctx context.Context, globalConfig *config.GlobalConfig, msg *pbchat.SendMsgReq) error {
|
||||
if !globalConfig.Callback.CallbackBeforeSendSingleMsg.Enable || msg.MsgData.ContentType == constant.Typing {
|
||||
func (m *msgServer) webhookBeforeSendSingleMsg(ctx context.Context, before *config.BeforeConfig, msg *pbchat.SendMsgReq) error {
|
||||
return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
|
||||
if msg.MsgData.ContentType == constant.Typing {
|
||||
return nil
|
||||
}
|
||||
cbReq := &cbapi.CallbackBeforeSendSingleMsgReq{
|
||||
CommonCallbackReq: toCommonCallback(ctx, msg, cbapi.CallbackBeforeSendSingleMsgCommand),
|
||||
RecvID: msg.MsgData.RecvID,
|
||||
}
|
||||
resp := &cbapi.CallbackBeforeSendSingleMsgResp{}
|
||||
if err := m.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
req := &cbapi.CallbackBeforeSendSingleMsgReq{
|
||||
CommonCallbackReq: toCommonCallback(ctx, msg, cbapi.CallbackBeforeSendSingleMsgCommand),
|
||||
RecvID: msg.MsgData.RecvID,
|
||||
}
|
||||
resp := &cbapi.CallbackBeforeSendSingleMsgResp{}
|
||||
if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackBeforeSendSingleMsg); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func callbackAfterSendSingleMsg(ctx context.Context, globalConfig *config.GlobalConfig, msg *pbchat.SendMsgReq) error {
|
||||
if !globalConfig.Callback.CallbackAfterSendSingleMsg.Enable || msg.MsgData.ContentType == constant.Typing {
|
||||
return nil
|
||||
func (m *msgServer) webhookAfterSendSingleMsg(ctx context.Context, after *config.AfterConfig, msg *pbchat.SendMsgReq) {
|
||||
if msg.MsgData.ContentType == constant.Typing {
|
||||
return
|
||||
}
|
||||
req := &cbapi.CallbackAfterSendSingleMsgReq{
|
||||
cbReq := &cbapi.CallbackAfterSendSingleMsgReq{
|
||||
CommonCallbackReq: toCommonCallback(ctx, msg, cbapi.CallbackAfterSendSingleMsgCommand),
|
||||
RecvID: msg.MsgData.RecvID,
|
||||
}
|
||||
resp := &cbapi.CallbackAfterSendSingleMsgResp{}
|
||||
if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackAfterSendSingleMsg); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
m.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &cbapi.CallbackAfterSendSingleMsgResp{}, after)
|
||||
}
|
||||
|
||||
func callbackBeforeSendGroupMsg(ctx context.Context, globalConfig *config.GlobalConfig, msg *pbchat.SendMsgReq) error {
|
||||
if !globalConfig.Callback.CallbackBeforeSendGroupMsg.Enable || msg.MsgData.ContentType == constant.Typing {
|
||||
func (m *msgServer) webhookBeforeSendGroupMsg(ctx context.Context, before *config.BeforeConfig, msg *pbchat.SendMsgReq) error {
|
||||
return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
|
||||
if msg.MsgData.ContentType == constant.Typing {
|
||||
return nil
|
||||
}
|
||||
cbReq := &cbapi.CallbackBeforeSendGroupMsgReq{
|
||||
CommonCallbackReq: toCommonCallback(ctx, msg, cbapi.CallbackBeforeSendGroupMsgCommand),
|
||||
GroupID: msg.MsgData.GroupID,
|
||||
}
|
||||
resp := &cbapi.CallbackBeforeSendGroupMsgResp{}
|
||||
if err := m.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
req := &cbapi.CallbackBeforeSendGroupMsgReq{
|
||||
CommonCallbackReq: toCommonCallback(ctx, msg, cbapi.CallbackBeforeSendGroupMsgCommand),
|
||||
GroupID: msg.MsgData.GroupID,
|
||||
}
|
||||
resp := &cbapi.CallbackBeforeSendGroupMsgResp{}
|
||||
if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackBeforeSendGroupMsg); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func callbackAfterSendGroupMsg(ctx context.Context, globalConfig *config.GlobalConfig, msg *pbchat.SendMsgReq) error {
|
||||
if !globalConfig.Callback.CallbackAfterSendGroupMsg.Enable || msg.MsgData.ContentType == constant.Typing {
|
||||
return nil
|
||||
func (m *msgServer) webhookAfterSendGroupMsg(ctx context.Context, after *config.AfterConfig, msg *pbchat.SendMsgReq) {
|
||||
if msg.MsgData.ContentType == constant.Typing {
|
||||
return
|
||||
}
|
||||
req := &cbapi.CallbackAfterSendGroupMsgReq{
|
||||
cbReq := &cbapi.CallbackAfterSendGroupMsgReq{
|
||||
CommonCallbackReq: toCommonCallback(ctx, msg, cbapi.CallbackAfterSendGroupMsgCommand),
|
||||
GroupID: msg.MsgData.GroupID,
|
||||
}
|
||||
resp := &cbapi.CallbackAfterSendGroupMsgResp{}
|
||||
if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackAfterSendGroupMsg); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
m.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &cbapi.CallbackAfterSendGroupMsgResp{}, after)
|
||||
}
|
||||
|
||||
func callbackMsgModify(ctx context.Context, globalConfig *config.GlobalConfig, msg *pbchat.SendMsgReq) error {
|
||||
if !globalConfig.Callback.CallbackMsgModify.Enable || msg.MsgData.ContentType != constant.Text {
|
||||
return nil
|
||||
}
|
||||
req := &cbapi.CallbackMsgModifyCommandReq{
|
||||
CommonCallbackReq: toCommonCallback(ctx, msg, cbapi.CallbackMsgModifyCommand),
|
||||
}
|
||||
resp := &cbapi.CallbackMsgModifyCommandResp{}
|
||||
if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackMsgModify); err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.Content != nil {
|
||||
msg.MsgData.Content = []byte(*resp.Content)
|
||||
}
|
||||
utils.NotNilReplace(msg.MsgData.OfflinePushInfo, resp.OfflinePushInfo)
|
||||
utils.NotNilReplace(&msg.MsgData.RecvID, resp.RecvID)
|
||||
utils.NotNilReplace(&msg.MsgData.GroupID, resp.GroupID)
|
||||
utils.NotNilReplace(&msg.MsgData.ClientMsgID, resp.ClientMsgID)
|
||||
utils.NotNilReplace(&msg.MsgData.ServerMsgID, resp.ServerMsgID)
|
||||
utils.NotNilReplace(&msg.MsgData.SenderPlatformID, resp.SenderPlatformID)
|
||||
utils.NotNilReplace(&msg.MsgData.SenderNickname, resp.SenderNickname)
|
||||
utils.NotNilReplace(&msg.MsgData.SenderFaceURL, resp.SenderFaceURL)
|
||||
utils.NotNilReplace(&msg.MsgData.SessionType, resp.SessionType)
|
||||
utils.NotNilReplace(&msg.MsgData.MsgFrom, resp.MsgFrom)
|
||||
utils.NotNilReplace(&msg.MsgData.ContentType, resp.ContentType)
|
||||
utils.NotNilReplace(&msg.MsgData.Status, resp.Status)
|
||||
utils.NotNilReplace(&msg.MsgData.Options, resp.Options)
|
||||
utils.NotNilReplace(&msg.MsgData.AtUserIDList, resp.AtUserIDList)
|
||||
utils.NotNilReplace(&msg.MsgData.AttachedInfo, resp.AttachedInfo)
|
||||
utils.NotNilReplace(&msg.MsgData.Ex, resp.Ex)
|
||||
log.ZDebug(ctx, "callbackMsgModify", "msg", msg.MsgData)
|
||||
return nil
|
||||
}
|
||||
func CallbackGroupMsgRead(ctx context.Context, globalConfig *config.GlobalConfig, req *cbapi.CallbackGroupMsgReadReq) error {
|
||||
if !globalConfig.Callback.CallbackGroupMsgRead.Enable {
|
||||
return nil
|
||||
}
|
||||
req.CallbackCommand = cbapi.CallbackGroupMsgReadCommand
|
||||
func (m *msgServer) webhookBeforeMsgModify(ctx context.Context, before *config.BeforeConfig, msg *pbchat.SendMsgReq) error {
|
||||
return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
|
||||
if msg.MsgData.ContentType != constant.Text {
|
||||
return nil
|
||||
}
|
||||
cbReq := &cbapi.CallbackMsgModifyCommandReq{
|
||||
CommonCallbackReq: toCommonCallback(ctx, msg, cbapi.CallbackBeforeMsgModifyCommand),
|
||||
}
|
||||
resp := &cbapi.CallbackMsgModifyCommandResp{}
|
||||
if err := m.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp := &cbapi.CallbackGroupMsgReadResp{}
|
||||
if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackGroupMsgRead); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
if resp.Content != nil {
|
||||
msg.MsgData.Content = []byte(*resp.Content)
|
||||
}
|
||||
datautil.NotNilReplace(msg.MsgData.OfflinePushInfo, resp.OfflinePushInfo)
|
||||
datautil.NotNilReplace(&msg.MsgData.RecvID, resp.RecvID)
|
||||
datautil.NotNilReplace(&msg.MsgData.GroupID, resp.GroupID)
|
||||
datautil.NotNilReplace(&msg.MsgData.ClientMsgID, resp.ClientMsgID)
|
||||
datautil.NotNilReplace(&msg.MsgData.ServerMsgID, resp.ServerMsgID)
|
||||
datautil.NotNilReplace(&msg.MsgData.SenderPlatformID, resp.SenderPlatformID)
|
||||
datautil.NotNilReplace(&msg.MsgData.SenderNickname, resp.SenderNickname)
|
||||
datautil.NotNilReplace(&msg.MsgData.SenderFaceURL, resp.SenderFaceURL)
|
||||
datautil.NotNilReplace(&msg.MsgData.SessionType, resp.SessionType)
|
||||
datautil.NotNilReplace(&msg.MsgData.MsgFrom, resp.MsgFrom)
|
||||
datautil.NotNilReplace(&msg.MsgData.ContentType, resp.ContentType)
|
||||
datautil.NotNilReplace(&msg.MsgData.Status, resp.Status)
|
||||
datautil.NotNilReplace(&msg.MsgData.Options, resp.Options)
|
||||
datautil.NotNilReplace(&msg.MsgData.AtUserIDList, resp.AtUserIDList)
|
||||
datautil.NotNilReplace(&msg.MsgData.AttachedInfo, resp.AttachedInfo)
|
||||
datautil.NotNilReplace(&msg.MsgData.Ex, resp.Ex)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func CallbackSingleMsgRead(ctx context.Context, globalConfig *config.GlobalConfig, req *cbapi.CallbackSingleMsgReadReq) error {
|
||||
if !globalConfig.Callback.CallbackSingleMsgRead.Enable {
|
||||
return nil
|
||||
}
|
||||
req.CallbackCommand = cbapi.CallbackSingleMsgRead
|
||||
|
||||
resp := &cbapi.CallbackSingleMsgReadResp{}
|
||||
|
||||
if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackSingleMsgRead); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
func (m *msgServer) webhookAfterGroupMsgRead(ctx context.Context, after *config.AfterConfig, req *cbapi.CallbackGroupMsgReadReq) {
|
||||
req.CallbackCommand = cbapi.CallbackAfterGroupMsgReadCommand
|
||||
m.webhookClient.AsyncPost(ctx, req.GetCallbackCommand(), req, &cbapi.CallbackGroupMsgReadResp{}, after)
|
||||
}
|
||||
func CallbackAfterRevokeMsg(ctx context.Context, globalConfig *config.GlobalConfig, req *pbchat.RevokeMsgReq) error {
|
||||
if !globalConfig.Callback.CallbackAfterRevokeMsg.Enable {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *msgServer) webhookAfterSingleMsgRead(ctx context.Context, after *config.AfterConfig, req *cbapi.CallbackSingleMsgReadReq) {
|
||||
|
||||
req.CallbackCommand = cbapi.CallbackAfterSingleMsgReadCommand
|
||||
|
||||
m.webhookClient.AsyncPost(ctx, req.GetCallbackCommand(), req, &cbapi.CallbackSingleMsgReadResp{}, after)
|
||||
|
||||
}
|
||||
|
||||
func (m *msgServer) webhookAfterRevokeMsg(ctx context.Context, after *config.AfterConfig, req *pbchat.RevokeMsgReq) {
|
||||
callbackReq := &cbapi.CallbackAfterRevokeMsgReq{
|
||||
CallbackCommand: cbapi.CallbackAfterRevokeMsgCommand,
|
||||
ConversationID: req.ConversationID,
|
||||
Seq: req.Seq,
|
||||
UserID: req.UserID,
|
||||
}
|
||||
resp := &cbapi.CallbackAfterRevokeMsgResp{}
|
||||
if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, callbackReq, resp, globalConfig.Callback.CallbackAfterRevokeMsg); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
m.webhookClient.AsyncPost(ctx, callbackReq.GetCallbackCommand(), callbackReq, &cbapi.CallbackAfterRevokeMsgResp{}, after)
|
||||
}
|
||||
|
||||
+21
-55
@@ -17,13 +17,13 @@ package msg
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/OpenIMSDK/protocol/conversation"
|
||||
"github.com/OpenIMSDK/protocol/msg"
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
"github.com/openimsdk/protocol/conversation"
|
||||
"github.com/openimsdk/protocol/msg"
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/utils/timeutil"
|
||||
)
|
||||
|
||||
func (m *msgServer) getMinSeqs(maxSeqs map[string]int64) map[string]int64 {
|
||||
@@ -41,11 +41,8 @@ func (m *msgServer) validateDeleteSyncOpt(opt *msg.DeleteSyncOpt) (isSyncSelf, i
|
||||
return opt.IsSyncSelf, opt.IsSyncOther
|
||||
}
|
||||
|
||||
func (m *msgServer) ClearConversationsMsg(
|
||||
ctx context.Context,
|
||||
req *msg.ClearConversationsMsgReq,
|
||||
) (*msg.ClearConversationsMsgResp, error) {
|
||||
if err := authverify.CheckAccessV3(ctx, req.UserID, m.config); err != nil {
|
||||
func (m *msgServer) ClearConversationsMsg(ctx context.Context, req *msg.ClearConversationsMsgReq) (*msg.ClearConversationsMsgResp, error) {
|
||||
if err := authverify.CheckAccessV3(ctx, req.UserID, m.config.Share.IMAdminUserID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := m.clearConversation(ctx, req.ConversationIDs, req.UserID, req.DeleteSyncOpt); err != nil {
|
||||
@@ -54,18 +51,14 @@ func (m *msgServer) ClearConversationsMsg(
|
||||
return &msg.ClearConversationsMsgResp{}, nil
|
||||
}
|
||||
|
||||
func (m *msgServer) UserClearAllMsg(
|
||||
ctx context.Context,
|
||||
req *msg.UserClearAllMsgReq,
|
||||
) (*msg.UserClearAllMsgResp, error) {
|
||||
if err := authverify.CheckAccessV3(ctx, req.UserID, m.config); err != nil {
|
||||
func (m *msgServer) UserClearAllMsg(ctx context.Context, req *msg.UserClearAllMsgReq) (*msg.UserClearAllMsgResp, error) {
|
||||
if err := authverify.CheckAccessV3(ctx, req.UserID, m.config.Share.IMAdminUserID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conversationIDs, err := m.ConversationLocalCache.GetConversationIDs(ctx, req.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.ZDebug(ctx, "GetMaxSeq", "conversationIDs", conversationIDs)
|
||||
if err := m.clearConversation(ctx, conversationIDs, req.UserID, req.DeleteSyncOpt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -73,7 +66,7 @@ func (m *msgServer) UserClearAllMsg(
|
||||
}
|
||||
|
||||
func (m *msgServer) DeleteMsgs(ctx context.Context, req *msg.DeleteMsgsReq) (*msg.DeleteMsgsResp, error) {
|
||||
if err := authverify.CheckAccessV3(ctx, req.UserID, m.config); err != nil {
|
||||
if err := authverify.CheckAccessV3(ctx, req.UserID, m.config.Share.IMAdminUserID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
isSyncSelf, isSyncOther := m.validateDeleteSyncOpt(req.DeleteSyncOpt)
|
||||
@@ -86,7 +79,7 @@ func (m *msgServer) DeleteMsgs(ctx context.Context, req *msg.DeleteMsgsReq) (*ms
|
||||
return nil, err
|
||||
}
|
||||
tips := &sdkws.DeleteMsgsTips{UserID: req.UserID, ConversationID: req.ConversationID, Seqs: req.Seqs}
|
||||
m.notificationSender.NotificationWithSesstionType(
|
||||
m.notificationSender.NotificationWithSessionType(
|
||||
ctx,
|
||||
req.UserID,
|
||||
m.conversationAndGetRecvID(conversations[0], req.UserID),
|
||||
@@ -100,16 +93,13 @@ func (m *msgServer) DeleteMsgs(ctx context.Context, req *msg.DeleteMsgsReq) (*ms
|
||||
}
|
||||
if isSyncSelf {
|
||||
tips := &sdkws.DeleteMsgsTips{UserID: req.UserID, ConversationID: req.ConversationID, Seqs: req.Seqs}
|
||||
m.notificationSender.NotificationWithSesstionType(ctx, req.UserID, req.UserID, constant.DeleteMsgsNotification, constant.SingleChatType, tips)
|
||||
m.notificationSender.NotificationWithSessionType(ctx, req.UserID, req.UserID, constant.DeleteMsgsNotification, constant.SingleChatType, tips)
|
||||
}
|
||||
}
|
||||
return &msg.DeleteMsgsResp{}, nil
|
||||
}
|
||||
|
||||
func (m *msgServer) DeleteMsgPhysicalBySeq(
|
||||
ctx context.Context,
|
||||
req *msg.DeleteMsgPhysicalBySeqReq,
|
||||
) (*msg.DeleteMsgPhysicalBySeqResp, error) {
|
||||
func (m *msgServer) DeleteMsgPhysicalBySeq(ctx context.Context, req *msg.DeleteMsgPhysicalBySeqReq) (*msg.DeleteMsgPhysicalBySeqResp, error) {
|
||||
err := m.MsgDatabase.DeleteMsgsPhysicalBySeqs(ctx, req.ConversationID, req.Seqs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -117,37 +107,20 @@ func (m *msgServer) DeleteMsgPhysicalBySeq(
|
||||
return &msg.DeleteMsgPhysicalBySeqResp{}, nil
|
||||
}
|
||||
|
||||
func (m *msgServer) DeleteMsgPhysical(
|
||||
ctx context.Context,
|
||||
req *msg.DeleteMsgPhysicalReq,
|
||||
) (*msg.DeleteMsgPhysicalResp, error) {
|
||||
if err := authverify.CheckAdmin(ctx, m.config); err != nil {
|
||||
func (m *msgServer) DeleteMsgPhysical(ctx context.Context, req *msg.DeleteMsgPhysicalReq) (*msg.DeleteMsgPhysicalResp, error) {
|
||||
if err := authverify.CheckAdmin(ctx, m.config.Share.IMAdminUserID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
remainTime := utils.GetCurrentTimestampBySecond() - req.Timestamp
|
||||
remainTime := timeutil.GetCurrentTimestampBySecond() - req.Timestamp
|
||||
for _, conversationID := range req.ConversationIDs {
|
||||
if err := m.MsgDatabase.DeleteConversationMsgsAndSetMinSeq(ctx, conversationID, remainTime); err != nil {
|
||||
log.ZWarn(
|
||||
ctx,
|
||||
"DeleteConversationMsgsAndSetMinSeq error",
|
||||
err,
|
||||
"conversationID",
|
||||
conversationID,
|
||||
"err",
|
||||
err,
|
||||
)
|
||||
log.ZWarn(ctx, "DeleteConversationMsgsAndSetMinSeq error", err, "conversationID", conversationID, "err", err)
|
||||
}
|
||||
}
|
||||
return &msg.DeleteMsgPhysicalResp{}, nil
|
||||
}
|
||||
|
||||
func (m *msgServer) clearConversation(
|
||||
ctx context.Context,
|
||||
conversationIDs []string,
|
||||
userID string,
|
||||
deleteSyncOpt *msg.DeleteSyncOpt,
|
||||
) error {
|
||||
defer log.ZDebug(ctx, "clearConversation return line")
|
||||
func (m *msgServer) clearConversation(ctx context.Context, conversationIDs []string, userID string, deleteSyncOpt *msg.DeleteSyncOpt) error {
|
||||
conversations, err := m.Conversation.GetConversationsByConversationID(ctx, conversationIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -171,14 +144,7 @@ func (m *msgServer) clearConversation(
|
||||
// notification 2 self
|
||||
if isSyncSelf {
|
||||
tips := &sdkws.ClearConversationTips{UserID: userID, ConversationIDs: existConversationIDs}
|
||||
m.notificationSender.NotificationWithSesstionType(
|
||||
ctx,
|
||||
userID,
|
||||
userID,
|
||||
constant.ClearConversationNotification,
|
||||
constant.SingleChatType,
|
||||
tips,
|
||||
)
|
||||
m.notificationSender.NotificationWithSessionType(ctx, userID, userID, constant.ClearConversationNotification, constant.SingleChatType, tips)
|
||||
}
|
||||
} else {
|
||||
if err := m.MsgDatabase.SetMinSeqs(ctx, m.getMinSeqs(maxSeqs)); err != nil {
|
||||
@@ -186,7 +152,7 @@ func (m *msgServer) clearConversation(
|
||||
}
|
||||
for _, conversation := range existConversations {
|
||||
tips := &sdkws.ClearConversationTips{UserID: userID, ConversationIDs: []string{conversation.ConversationID}}
|
||||
m.notificationSender.NotificationWithSesstionType(ctx, userID, m.conversationAndGetRecvID(conversation, userID), constant.ClearConversationNotification, conversation.ConversationType, tips)
|
||||
m.notificationSender.NotificationWithSessionType(ctx, userID, m.conversationAndGetRecvID(conversation, userID), constant.ClearConversationNotification, conversation.ConversationType, tips)
|
||||
}
|
||||
}
|
||||
if err := m.MsgDatabase.UserSetHasReadSeqs(ctx, userID, maxSeqs); err != nil {
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package msg
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/OpenIMSDK/protocol/msg"
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
)
|
||||
|
||||
type MessageInterceptorFunc func(ctx context.Context, globalConfig *config.GlobalConfig, req *msg.SendMsgReq) (*sdkws.MsgData, error)
|
||||
|
||||
func MessageHasReadEnabled(_ context.Context, globalConfig *config.GlobalConfig, req *msg.SendMsgReq) (*sdkws.MsgData, error) {
|
||||
switch {
|
||||
case req.MsgData.ContentType == constant.HasReadReceipt && req.MsgData.SessionType == constant.SingleChatType:
|
||||
if !globalConfig.SingleMessageHasReadReceiptEnable {
|
||||
return nil, errs.ErrMessageHasReadDisable.Wrap()
|
||||
}
|
||||
return req.MsgData, nil
|
||||
case req.MsgData.ContentType == constant.HasReadReceipt && req.MsgData.SessionType == constant.SuperGroupChatType:
|
||||
if !globalConfig.GroupMessageHasReadReceiptEnable {
|
||||
return nil, errs.ErrMessageHasReadDisable.Wrap()
|
||||
}
|
||||
return req.MsgData, nil
|
||||
}
|
||||
return req.MsgData, nil
|
||||
}
|
||||
@@ -17,15 +17,12 @@ package msg
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
pbmsg "github.com/OpenIMSDK/protocol/msg"
|
||||
"github.com/OpenIMSDK/tools/mcontext"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
pbmsg "github.com/openimsdk/protocol/msg"
|
||||
"github.com/openimsdk/tools/mcontext"
|
||||
)
|
||||
|
||||
func (m *msgServer) SetSendMsgStatus(
|
||||
ctx context.Context,
|
||||
req *pbmsg.SetSendMsgStatusReq,
|
||||
) (*pbmsg.SetSendMsgStatusResp, error) {
|
||||
func (m *msgServer) SetSendMsgStatus(ctx context.Context, req *pbmsg.SetSendMsgStatusReq) (*pbmsg.SetSendMsgStatusResp, error) {
|
||||
resp := &pbmsg.SetSendMsgStatusResp{}
|
||||
if err := m.MsgDatabase.SetSendMsgStatus(ctx, mcontext.GetOperationID(ctx), req.Status); err != nil {
|
||||
return nil, err
|
||||
@@ -33,10 +30,7 @@ func (m *msgServer) SetSendMsgStatus(
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (m *msgServer) GetSendMsgStatus(
|
||||
ctx context.Context,
|
||||
req *pbmsg.GetSendMsgStatusReq,
|
||||
) (*pbmsg.GetSendMsgStatusResp, error) {
|
||||
func (m *msgServer) GetSendMsgStatus(ctx context.Context, req *pbmsg.GetSendMsgStatusReq) (*pbmsg.GetSendMsgStatusResp, error) {
|
||||
resp := &pbmsg.GetSendMsgStatusResp{}
|
||||
status, err := m.MsgDatabase.GetSendMsgStatus(ctx, mcontext.GetOperationID(ctx))
|
||||
if IsNotFound(err) {
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package msg
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
)
|
||||
|
||||
type MsgNotificationSender struct {
|
||||
*rpcclient.NotificationSender
|
||||
}
|
||||
|
||||
func NewMsgNotificationSender(config *Config, opts ...rpcclient.NotificationSenderOptions) *MsgNotificationSender {
|
||||
return &MsgNotificationSender{rpcclient.NewNotificationSender(&config.NotificationConfig, opts...)}
|
||||
}
|
||||
|
||||
func (m *MsgNotificationSender) UserDeleteMsgsNotification(ctx context.Context, userID, conversationID string, seqs []int64) {
|
||||
tips := sdkws.DeleteMsgsTips{
|
||||
UserID: userID,
|
||||
ConversationID: conversationID,
|
||||
Seqs: seqs,
|
||||
}
|
||||
m.Notification(ctx, userID, userID, constant.DeleteMsgsNotification, &tips)
|
||||
}
|
||||
|
||||
func (m *MsgNotificationSender) MarkAsReadNotification(ctx context.Context, conversationID string, sessionType int32, sendID, recvID string, seqs []int64, hasReadSeq int64) {
|
||||
tips := &sdkws.MarkAsReadTips{
|
||||
MarkAsReadUserID: sendID,
|
||||
ConversationID: conversationID,
|
||||
Seqs: seqs,
|
||||
HasReadSeq: hasReadSeq,
|
||||
}
|
||||
m.NotificationWithSessionType(ctx, sendID, recvID, constant.HasReadReceipt, sessionType, tips)
|
||||
}
|
||||
+30
-36
@@ -19,29 +19,29 @@ import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/OpenIMSDK/protocol/msg"
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/OpenIMSDK/tools/mcontext"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
|
||||
unrelationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
"github.com/openimsdk/protocol/msg"
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/mcontext"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
)
|
||||
|
||||
func (m *msgServer) RevokeMsg(ctx context.Context, req *msg.RevokeMsgReq) (*msg.RevokeMsgResp, error) {
|
||||
defer log.ZDebug(ctx, "RevokeMsg return line")
|
||||
if req.UserID == "" {
|
||||
return nil, errs.ErrArgs.Wrap("user_id is empty")
|
||||
return nil, errs.ErrArgs.WrapMsg("user_id is empty")
|
||||
}
|
||||
if req.ConversationID == "" {
|
||||
return nil, errs.ErrArgs.Wrap("conversation_id is empty")
|
||||
return nil, errs.ErrArgs.WrapMsg("conversation_id is empty")
|
||||
}
|
||||
if req.Seq < 0 {
|
||||
return nil, errs.ErrArgs.Wrap("seq is invalid")
|
||||
return nil, errs.ErrArgs.WrapMsg("seq is invalid")
|
||||
}
|
||||
if err := authverify.CheckAccessV3(ctx, req.UserID, m.config); err != nil {
|
||||
if err := authverify.CheckAccessV3(ctx, req.UserID, m.config.Share.IMAdminUserID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user, err := m.UserLocalCache.GetUserInfo(ctx, req.UserID)
|
||||
@@ -53,24 +53,24 @@ func (m *msgServer) RevokeMsg(ctx context.Context, req *msg.RevokeMsgReq) (*msg.
|
||||
return nil, err
|
||||
}
|
||||
if len(msgs) == 0 || msgs[0] == nil {
|
||||
return nil, errs.ErrRecordNotFound.Wrap("msg not found")
|
||||
return nil, errs.ErrRecordNotFound.WrapMsg("msg not found")
|
||||
}
|
||||
if msgs[0].ContentType == constant.MsgRevokeNotification {
|
||||
return nil, errs.ErrMsgAlreadyRevoke.Wrap("msg already revoke")
|
||||
return nil, servererrs.ErrMsgAlreadyRevoke.WrapMsg("msg already revoke")
|
||||
}
|
||||
|
||||
data, _ := json.Marshal(msgs[0])
|
||||
log.ZInfo(ctx, "GetMsgBySeqs", "conversationID", req.ConversationID, "seq", req.Seq, "msg", string(data))
|
||||
log.ZDebug(ctx, "GetMsgBySeqs", "conversationID", req.ConversationID, "seq", req.Seq, "msg", string(data))
|
||||
var role int32
|
||||
if !authverify.IsAppManagerUid(ctx, m.config) {
|
||||
if !authverify.IsAppManagerUid(ctx, m.config.Share.IMAdminUserID) {
|
||||
switch msgs[0].SessionType {
|
||||
case constant.SingleChatType:
|
||||
if err := authverify.CheckAccessV3(ctx, msgs[0].SendID, m.config); err != nil {
|
||||
if err := authverify.CheckAccessV3(ctx, msgs[0].SendID, m.config.Share.IMAdminUserID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
role = user.AppMangerLevel
|
||||
case constant.SuperGroupChatType:
|
||||
members, err := m.GroupLocalCache.GetGroupMemberInfoMap(ctx, msgs[0].GroupID, utils.Distinct([]string{req.UserID, msgs[0].SendID}))
|
||||
case constant.ReadGroupChatType:
|
||||
members, err := m.GroupLocalCache.GetGroupMemberInfoMap(ctx, msgs[0].GroupID, datautil.Distinct([]string{req.UserID, msgs[0].SendID}))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -79,21 +79,21 @@ func (m *msgServer) RevokeMsg(ctx context.Context, req *msg.RevokeMsgReq) (*msg.
|
||||
case constant.GroupOwner:
|
||||
case constant.GroupAdmin:
|
||||
if members[msgs[0].SendID].RoleLevel != constant.GroupOrdinaryUsers {
|
||||
return nil, errs.ErrNoPermission.Wrap("no permission")
|
||||
return nil, errs.ErrNoPermission.WrapMsg("no permission")
|
||||
}
|
||||
default:
|
||||
return nil, errs.ErrNoPermission.Wrap("no permission")
|
||||
return nil, errs.ErrNoPermission.WrapMsg("no permission")
|
||||
}
|
||||
}
|
||||
if member := members[req.UserID]; member != nil {
|
||||
role = member.RoleLevel
|
||||
}
|
||||
default:
|
||||
return nil, errs.ErrInternalServer.Wrap("msg sessionType not supported")
|
||||
return nil, errs.ErrInternalServer.WrapMsg("msg sessionType not supported")
|
||||
}
|
||||
}
|
||||
now := time.Now().UnixMilli()
|
||||
err = m.MsgDatabase.RevokeMsg(ctx, req.ConversationID, req.Seq, &unrelationtb.RevokeModel{
|
||||
err = m.MsgDatabase.RevokeMsg(ctx, req.ConversationID, req.Seq, &relation.RevokeModel{
|
||||
Role: role,
|
||||
UserID: req.UserID,
|
||||
Nickname: user.Nickname,
|
||||
@@ -104,11 +104,9 @@ func (m *msgServer) RevokeMsg(ctx context.Context, req *msg.RevokeMsgReq) (*msg.
|
||||
}
|
||||
revokerUserID := mcontext.GetOpUserID(ctx)
|
||||
var flag bool
|
||||
if len(m.config.Manager.UserID) > 0 {
|
||||
flag = utils.Contain(revokerUserID, m.config.Manager.UserID...)
|
||||
}
|
||||
if len(m.config.Manager.UserID) == 0 && len(m.config.IMAdmin.UserID) > 0 {
|
||||
flag = utils.Contain(revokerUserID, m.config.IMAdmin.UserID...)
|
||||
|
||||
if len(m.config.Share.IMAdminUserID) > 0 {
|
||||
flag = datautil.Contain(revokerUserID, m.config.Share.IMAdminUserID...)
|
||||
}
|
||||
tips := sdkws.RevokeMsgTips{
|
||||
RevokerUserID: revokerUserID,
|
||||
@@ -120,16 +118,12 @@ func (m *msgServer) RevokeMsg(ctx context.Context, req *msg.RevokeMsgReq) (*msg.
|
||||
IsAdminRevoke: flag,
|
||||
}
|
||||
var recvID string
|
||||
if msgs[0].SessionType == constant.SuperGroupChatType {
|
||||
if msgs[0].SessionType == constant.ReadGroupChatType {
|
||||
recvID = msgs[0].GroupID
|
||||
} else {
|
||||
recvID = msgs[0].RecvID
|
||||
}
|
||||
if err := m.notificationSender.NotificationWithSesstionType(ctx, req.UserID, recvID, constant.MsgRevokeNotification, msgs[0].SessionType, &tips); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = CallbackAfterRevokeMsg(ctx, m.config, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m.notificationSender.NotificationWithSessionType(ctx, req.UserID, recvID, constant.MsgRevokeNotification, msgs[0].SessionType, &tips)
|
||||
m.webhookAfterRevokeMsg(ctx, &m.config.WebhooksConfig.AfterRevokeMsg, req)
|
||||
return &msg.RevokeMsgResp{}, nil
|
||||
}
|
||||
|
||||
+44
-65
@@ -17,67 +17,59 @@ package msg
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
pbconversation "github.com/OpenIMSDK/protocol/conversation"
|
||||
pbmsg "github.com/OpenIMSDK/protocol/msg"
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
"github.com/OpenIMSDK/protocol/wrapperspb"
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/OpenIMSDK/tools/mcontext"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/util/conversationutil"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
pbconversation "github.com/openimsdk/protocol/conversation"
|
||||
pbmsg "github.com/openimsdk/protocol/msg"
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
"github.com/openimsdk/protocol/wrapperspb"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/mcontext"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
"github.com/openimsdk/tools/utils/stringutil"
|
||||
)
|
||||
|
||||
func (m *msgServer) SendMsg(ctx context.Context, req *pbmsg.SendMsgReq) (resp *pbmsg.SendMsgResp, error error) {
|
||||
resp = &pbmsg.SendMsgResp{}
|
||||
func (m *msgServer) SendMsg(ctx context.Context, req *pbmsg.SendMsgReq) (*pbmsg.SendMsgResp, error) {
|
||||
if req.MsgData != nil {
|
||||
flag := isMessageHasReadEnabled(req.MsgData, m.config)
|
||||
if !flag {
|
||||
return nil, errs.ErrMessageHasReadDisable.Wrap()
|
||||
}
|
||||
m.encapsulateMsgData(req.MsgData)
|
||||
switch req.MsgData.SessionType {
|
||||
case constant.SingleChatType:
|
||||
return m.sendMsgSingleChat(ctx, req)
|
||||
case constant.NotificationChatType:
|
||||
return m.sendMsgNotification(ctx, req)
|
||||
case constant.SuperGroupChatType:
|
||||
case constant.ReadGroupChatType:
|
||||
return m.sendMsgSuperGroupChat(ctx, req)
|
||||
default:
|
||||
return nil, errs.ErrArgs.Wrap("unknown sessionType")
|
||||
return nil, errs.ErrArgs.WrapMsg("unknown sessionType")
|
||||
}
|
||||
} else {
|
||||
return nil, errs.ErrArgs.Wrap("msgData is nil")
|
||||
}
|
||||
return nil, errs.ErrArgs.WrapMsg("msgData is nil")
|
||||
}
|
||||
|
||||
func (m *msgServer) sendMsgSuperGroupChat(
|
||||
ctx context.Context,
|
||||
req *pbmsg.SendMsgReq,
|
||||
) (resp *pbmsg.SendMsgResp, err error) {
|
||||
func (m *msgServer) sendMsgSuperGroupChat(ctx context.Context, req *pbmsg.SendMsgReq) (resp *pbmsg.SendMsgResp, err error) {
|
||||
if err = m.messageVerification(ctx, req); err != nil {
|
||||
prommetrics.GroupChatMsgProcessFailedCounter.Inc()
|
||||
return nil, err
|
||||
}
|
||||
if err = callbackBeforeSendGroupMsg(ctx, m.config, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := callbackMsgModify(ctx, m.config, req); err != nil {
|
||||
if err = m.webhookBeforeSendGroupMsg(ctx, &m.config.WebhooksConfig.BeforeSendGroupMsg, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = m.MsgDatabase.MsgToMQ(ctx, utils.GenConversationUniqueKeyForGroup(req.MsgData.GroupID), req.MsgData)
|
||||
if err := m.webhookBeforeMsgModify(ctx, &m.config.WebhooksConfig.BeforeMsgModify, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = m.MsgDatabase.MsgToMQ(ctx, conversationutil.GenConversationUniqueKeyForGroup(req.MsgData.GroupID), req.MsgData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if req.MsgData.ContentType == constant.AtText {
|
||||
go m.setConversationAtInfo(ctx, req.MsgData)
|
||||
}
|
||||
if err = callbackAfterSendGroupMsg(ctx, m.config, req); err != nil {
|
||||
log.ZWarn(ctx, "CallbackAfterSendGroupMsg", err)
|
||||
}
|
||||
|
||||
m.webhookAfterSendGroupMsg(ctx, &m.config.WebhooksConfig.AfterSendGroupMsg, req)
|
||||
prommetrics.GroupChatMsgProcessSuccessCounter.Inc()
|
||||
resp = &pbmsg.SendMsgResp{}
|
||||
resp.SendTime = req.MsgData.SendTime
|
||||
@@ -95,43 +87,39 @@ func (m *msgServer) setConversationAtInfo(nctx context.Context, msg *sdkws.MsgDa
|
||||
ConversationType: msg.SessionType,
|
||||
GroupID: msg.GroupID,
|
||||
}
|
||||
tagAll := utils.IsContain(constant.AtAllString, msg.AtUserIDList)
|
||||
tagAll := datautil.Contain(constant.AtAllString, msg.AtUserIDList...)
|
||||
if tagAll {
|
||||
memberUserIDList, err := m.GroupLocalCache.GetGroupMemberIDs(ctx, msg.GroupID)
|
||||
if err != nil {
|
||||
log.ZWarn(ctx, "GetGroupMemberIDs", err)
|
||||
return
|
||||
}
|
||||
atUserID = utils.DifferenceString([]string{constant.AtAllString}, msg.AtUserIDList)
|
||||
atUserID = stringutil.DifferenceString([]string{constant.AtAllString}, msg.AtUserIDList)
|
||||
if len(atUserID) == 0 { // just @everyone
|
||||
conversation.GroupAtType = &wrapperspb.Int32Value{Value: constant.AtAll}
|
||||
} else { //@Everyone and @other people
|
||||
} else { // @Everyone and @other people
|
||||
conversation.GroupAtType = &wrapperspb.Int32Value{Value: constant.AtAllAtMe}
|
||||
err = m.Conversation.SetConversations(ctx, atUserID, conversation)
|
||||
if err != nil {
|
||||
log.ZWarn(ctx, "SetConversations", err, "userID", atUserID, "conversation", conversation)
|
||||
}
|
||||
memberUserIDList = utils.DifferenceString(atUserID, memberUserIDList)
|
||||
memberUserIDList = stringutil.DifferenceString(atUserID, memberUserIDList)
|
||||
}
|
||||
conversation.GroupAtType = &wrapperspb.Int32Value{Value: constant.AtAll}
|
||||
err = m.Conversation.SetConversations(ctx, memberUserIDList, conversation)
|
||||
if err != nil {
|
||||
log.ZWarn(ctx, "SetConversations", err, "userID", memberUserIDList, "conversation", conversation)
|
||||
}
|
||||
} else {
|
||||
conversation.GroupAtType = &wrapperspb.Int32Value{Value: constant.AtMe}
|
||||
err := m.Conversation.SetConversations(ctx, msg.AtUserIDList, conversation)
|
||||
if err != nil {
|
||||
log.ZWarn(ctx, "SetConversations", err, msg.AtUserIDList, conversation)
|
||||
}
|
||||
}
|
||||
conversation.GroupAtType = &wrapperspb.Int32Value{Value: constant.AtMe}
|
||||
err := m.Conversation.SetConversations(ctx, msg.AtUserIDList, conversation)
|
||||
if err != nil {
|
||||
log.ZWarn(ctx, "SetConversations", err, msg.AtUserIDList, conversation)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *msgServer) sendMsgNotification(
|
||||
ctx context.Context,
|
||||
req *pbmsg.SendMsgReq,
|
||||
) (resp *pbmsg.SendMsgResp, err error) {
|
||||
if err := m.MsgDatabase.MsgToMQ(ctx, utils.GenConversationUniqueKeyForSingle(req.MsgData.SendID, req.MsgData.RecvID), req.MsgData); err != nil {
|
||||
func (m *msgServer) sendMsgNotification(ctx context.Context, req *pbmsg.SendMsgReq) (resp *pbmsg.SendMsgResp, err error) {
|
||||
if err := m.MsgDatabase.MsgToMQ(ctx, conversationutil.GenConversationUniqueKeyForSingle(req.MsgData.SendID, req.MsgData.RecvID), req.MsgData); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp = &pbmsg.SendMsgResp{
|
||||
@@ -143,7 +131,6 @@ func (m *msgServer) sendMsgNotification(
|
||||
}
|
||||
|
||||
func (m *msgServer) sendMsgSingleChat(ctx context.Context, req *pbmsg.SendMsgReq) (resp *pbmsg.SendMsgResp, err error) {
|
||||
log.ZDebug(ctx, "sendMsgSingleChat return")
|
||||
if err := m.messageVerification(ctx, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -153,7 +140,7 @@ func (m *msgServer) sendMsgSingleChat(ctx context.Context, req *pbmsg.SendMsgReq
|
||||
isSend, err = m.modifyMessageByUserMessageReceiveOpt(
|
||||
ctx,
|
||||
req.MsgData.RecvID,
|
||||
utils.GenConversationIDForSingle(req.MsgData.SendID, req.MsgData.RecvID),
|
||||
conversationutil.GenConversationIDForSingle(req.MsgData.SendID, req.MsgData.RecvID),
|
||||
constant.SingleChatType,
|
||||
req,
|
||||
)
|
||||
@@ -165,31 +152,23 @@ func (m *msgServer) sendMsgSingleChat(ctx context.Context, req *pbmsg.SendMsgReq
|
||||
prommetrics.SingleChatMsgProcessFailedCounter.Inc()
|
||||
return nil, nil
|
||||
} else {
|
||||
if err = callbackBeforeSendSingleMsg(ctx, m.config, req); err != nil {
|
||||
if err = m.webhookBeforeSendSingleMsg(ctx, &m.config.WebhooksConfig.BeforeSendSingleMsg, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := m.webhookBeforeMsgModify(ctx, &m.config.WebhooksConfig.BeforeMsgModify, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := callbackMsgModify(ctx, m.config, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := m.MsgDatabase.MsgToMQ(ctx, utils.GenConversationUniqueKeyForSingle(req.MsgData.SendID, req.MsgData.RecvID), req.MsgData); err != nil {
|
||||
if err := m.MsgDatabase.MsgToMQ(ctx, conversationutil.GenConversationUniqueKeyForSingle(req.MsgData.SendID, req.MsgData.RecvID), req.MsgData); err != nil {
|
||||
prommetrics.SingleChatMsgProcessFailedCounter.Inc()
|
||||
return nil, err
|
||||
}
|
||||
err = callbackAfterSendSingleMsg(ctx, m.config, req)
|
||||
if err != nil {
|
||||
log.ZWarn(ctx, "CallbackAfterSendSingleMsg", err, "req", req)
|
||||
}
|
||||
resp = &pbmsg.SendMsgResp{
|
||||
m.webhookAfterSendSingleMsg(ctx, &m.config.WebhooksConfig.AfterSendSingleMsg, req)
|
||||
prommetrics.SingleChatMsgProcessSuccessCounter.Inc()
|
||||
return &pbmsg.SendMsgResp{
|
||||
ServerMsgID: req.MsgData.ServerMsgID,
|
||||
ClientMsgID: req.MsgData.ClientMsgID,
|
||||
SendTime: req.MsgData.SendTime,
|
||||
}
|
||||
prommetrics.SingleChatMsgProcessSuccessCounter.Inc()
|
||||
return resp, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (m *msgServer) BatchSendMsg(ctx context.Context, in *pbmsg.BatchSendMessageReq) (*pbmsg.BatchSendMessageResp, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -17,13 +17,10 @@ package msg
|
||||
import (
|
||||
"context"
|
||||
|
||||
pbmsg "github.com/OpenIMSDK/protocol/msg"
|
||||
pbmsg "github.com/openimsdk/protocol/msg"
|
||||
)
|
||||
|
||||
func (m *msgServer) GetConversationMaxSeq(
|
||||
ctx context.Context,
|
||||
req *pbmsg.GetConversationMaxSeqReq,
|
||||
) (resp *pbmsg.GetConversationMaxSeqResp, err error) {
|
||||
func (m *msgServer) GetConversationMaxSeq(ctx context.Context, req *pbmsg.GetConversationMaxSeqReq) (*pbmsg.GetConversationMaxSeqResp, error) {
|
||||
maxSeq, err := m.MsgDatabase.GetMaxSeq(ctx, req.ConversationID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
+66
-50
@@ -15,69 +15,84 @@
|
||||
package msg
|
||||
|
||||
import (
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/OpenIMSDK/protocol/conversation"
|
||||
"github.com/OpenIMSDK/protocol/msg"
|
||||
"github.com/OpenIMSDK/tools/discoveryregistry"
|
||||
"context"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/webhook"
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"github.com/openimsdk/tools/db/redisutil"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpccache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
"github.com/openimsdk/protocol/conversation"
|
||||
"github.com/openimsdk/protocol/msg"
|
||||
"github.com/openimsdk/tools/discovery"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type MessageInterceptorFunc func(ctx context.Context, globalConfig *Config, req *msg.SendMsgReq) (*sdkws.MsgData, error)
|
||||
type (
|
||||
// MessageInterceptorChain defines a chain of message interceptor functions.
|
||||
MessageInterceptorChain []MessageInterceptorFunc
|
||||
msgServer struct {
|
||||
RegisterCenter discoveryregistry.SvcDiscoveryRegistry
|
||||
MsgDatabase controller.CommonMsgDatabase
|
||||
Conversation *rpcclient.ConversationRpcClient
|
||||
UserLocalCache *rpccache.UserLocalCache
|
||||
FriendLocalCache *rpccache.FriendLocalCache
|
||||
GroupLocalCache *rpccache.GroupLocalCache
|
||||
ConversationLocalCache *rpccache.ConversationLocalCache
|
||||
Handlers MessageInterceptorChain
|
||||
notificationSender *rpcclient.NotificationSender
|
||||
config *config.GlobalConfig
|
||||
|
||||
// MsgServer encapsulates dependencies required for message handling.
|
||||
msgServer struct {
|
||||
RegisterCenter discovery.SvcDiscoveryRegistry // Service discovery registry for service registration.
|
||||
MsgDatabase controller.CommonMsgDatabase // Interface for message database operations.
|
||||
Conversation *rpcclient.ConversationRpcClient // RPC client for conversation service.
|
||||
UserLocalCache *rpccache.UserLocalCache // Local cache for user data.
|
||||
FriendLocalCache *rpccache.FriendLocalCache // Local cache for friend data.
|
||||
GroupLocalCache *rpccache.GroupLocalCache // Local cache for group data.
|
||||
ConversationLocalCache *rpccache.ConversationLocalCache // Local cache for conversation data.
|
||||
Handlers MessageInterceptorChain // Chain of handlers for processing messages.
|
||||
notificationSender *rpcclient.NotificationSender // RPC client for sending notifications.
|
||||
config *Config // Global configuration settings.
|
||||
webhookClient *webhook.Client
|
||||
}
|
||||
|
||||
Config struct {
|
||||
RpcConfig config.Msg
|
||||
RedisConfig config.Redis
|
||||
MongodbConfig config.Mongo
|
||||
KafkaConfig config.Kafka
|
||||
ZookeeperConfig config.ZooKeeper
|
||||
NotificationConfig config.Notification
|
||||
Share config.Share
|
||||
WebhooksConfig config.Webhooks
|
||||
LocalCacheConfig config.LocalCache
|
||||
}
|
||||
)
|
||||
|
||||
func (m *msgServer) addInterceptorHandler(interceptorFunc ...MessageInterceptorFunc) {
|
||||
m.Handlers = append(m.Handlers, interceptorFunc...)
|
||||
|
||||
}
|
||||
|
||||
//func (m *msgServer) execInterceptorHandler(ctx context.Context, config *config.GlobalConfig, req *msg.SendMsgReq) error {
|
||||
// for _, handler := range m.Handlers {
|
||||
// msgData, err := handler(ctx, config, req)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// req.MsgData = msgData
|
||||
// }
|
||||
// return nil
|
||||
//}
|
||||
|
||||
func Start(config *config.GlobalConfig, client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error {
|
||||
rdb, err := cache.NewRedis(config)
|
||||
func Start(ctx context.Context, config *Config, client discovery.SvcDiscoveryRegistry, server *grpc.Server) error {
|
||||
mgocli, err := mongoutil.NewMongoDB(ctx, config.MongodbConfig.Build())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mongo, err := unrelation.NewMongo(config)
|
||||
rdb, err := redisutil.NewRedisClient(ctx, config.RedisConfig.Build())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := mongo.CreateMsgIndex(); err != nil {
|
||||
msgDocModel, err := mgo.NewMsgMongo(mgocli.GetDB())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cacheModel := cache.NewMsgCacheModel(rdb, config)
|
||||
msgDocModel := unrelation.NewMsgMongoDriver(mongo.GetDatabase(config.Mongo.Database))
|
||||
conversationClient := rpcclient.NewConversationRpcClient(client, config)
|
||||
userRpcClient := rpcclient.NewUserRpcClient(client, config)
|
||||
groupRpcClient := rpcclient.NewGroupRpcClient(client, config)
|
||||
friendRpcClient := rpcclient.NewFriendRpcClient(client, config)
|
||||
msgDatabase, err := controller.NewCommonMsgDatabase(msgDocModel, cacheModel, config)
|
||||
//todo MsgCacheTimeout
|
||||
msgModel := cache.NewMsgCache(rdb, config.RedisConfig.EnablePipeline)
|
||||
seqModel := cache.NewSeqCache(rdb)
|
||||
conversationClient := rpcclient.NewConversationRpcClient(client, config.Share.RpcRegisterName.Conversation)
|
||||
userRpcClient := rpcclient.NewUserRpcClient(client, config.Share.RpcRegisterName.User, config.Share.IMAdminUserID)
|
||||
groupRpcClient := rpcclient.NewGroupRpcClient(client, config.Share.RpcRegisterName.Group)
|
||||
friendRpcClient := rpcclient.NewFriendRpcClient(client, config.Share.RpcRegisterName.Friend)
|
||||
msgDatabase, err := controller.NewCommonMsgDatabase(msgDocModel, msgModel, seqModel, &config.KafkaConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -85,28 +100,29 @@ func Start(config *config.GlobalConfig, client discoveryregistry.SvcDiscoveryReg
|
||||
Conversation: &conversationClient,
|
||||
MsgDatabase: msgDatabase,
|
||||
RegisterCenter: client,
|
||||
UserLocalCache: rpccache.NewUserLocalCache(userRpcClient, rdb),
|
||||
GroupLocalCache: rpccache.NewGroupLocalCache(groupRpcClient, rdb),
|
||||
ConversationLocalCache: rpccache.NewConversationLocalCache(conversationClient, rdb),
|
||||
FriendLocalCache: rpccache.NewFriendLocalCache(friendRpcClient, rdb),
|
||||
UserLocalCache: rpccache.NewUserLocalCache(userRpcClient, &config.LocalCacheConfig, rdb),
|
||||
GroupLocalCache: rpccache.NewGroupLocalCache(groupRpcClient, &config.LocalCacheConfig, rdb),
|
||||
ConversationLocalCache: rpccache.NewConversationLocalCache(conversationClient, &config.LocalCacheConfig, rdb),
|
||||
FriendLocalCache: rpccache.NewFriendLocalCache(friendRpcClient, &config.LocalCacheConfig, rdb),
|
||||
config: config,
|
||||
webhookClient: webhook.NewWebhookClient(config.WebhooksConfig.URL),
|
||||
}
|
||||
s.notificationSender = rpcclient.NewNotificationSender(config, rpcclient.WithLocalSendMsg(s.SendMsg))
|
||||
s.addInterceptorHandler(MessageHasReadEnabled)
|
||||
|
||||
s.notificationSender = rpcclient.NewNotificationSender(&config.NotificationConfig, rpcclient.WithLocalSendMsg(s.SendMsg))
|
||||
msg.RegisterMsgServer(server, s)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *msgServer) conversationAndGetRecvID(conversation *conversation.Conversation, userID string) (recvID string) {
|
||||
func (m *msgServer) conversationAndGetRecvID(conversation *conversation.Conversation, userID string) string {
|
||||
if conversation.ConversationType == constant.SingleChatType ||
|
||||
conversation.ConversationType == constant.NotificationChatType {
|
||||
if userID == conversation.OwnerUserID {
|
||||
recvID = conversation.UserID
|
||||
return conversation.UserID
|
||||
} else {
|
||||
recvID = conversation.OwnerUserID
|
||||
return conversation.OwnerUserID
|
||||
}
|
||||
} else if conversation.ConversationType == constant.SuperGroupChatType {
|
||||
recvID = conversation.GroupID
|
||||
} else if conversation.ConversationType == constant.ReadGroupChatType {
|
||||
return conversation.GroupID
|
||||
}
|
||||
return
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -18,28 +18,20 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/msg"
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
|
||||
"github.com/openimsdk/protocol/msg"
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
)
|
||||
|
||||
func (m *msgServer) GetActiveUser(ctx context.Context, req *msg.GetActiveUserReq) (*msg.GetActiveUserResp, error) {
|
||||
msgCount, userCount, users, dateCount, err := m.MsgDatabase.RangeUserSendCount(
|
||||
ctx,
|
||||
time.UnixMilli(req.Start),
|
||||
time.UnixMilli(req.End),
|
||||
req.Group,
|
||||
req.Ase,
|
||||
req.Pagination.PageNumber,
|
||||
req.Pagination.ShowNumber,
|
||||
)
|
||||
msgCount, userCount, users, dateCount, err := m.MsgDatabase.RangeUserSendCount(ctx, time.UnixMilli(req.Start), time.UnixMilli(req.End), req.Group, req.Ase, req.Pagination.PageNumber, req.Pagination.ShowNumber)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var pbUsers []*msg.ActiveUser
|
||||
if len(users) > 0 {
|
||||
userIDs := utils.Slice(users, func(e *unrelation.UserCount) string { return e.UserID })
|
||||
userIDs := datautil.Slice(users, func(e *relation.UserCount) string { return e.UserID })
|
||||
userMap, err := m.UserLocalCache.GetUsersInfoMap(ctx, userIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -68,20 +60,13 @@ func (m *msgServer) GetActiveUser(ctx context.Context, req *msg.GetActiveUserReq
|
||||
}
|
||||
|
||||
func (m *msgServer) GetActiveGroup(ctx context.Context, req *msg.GetActiveGroupReq) (*msg.GetActiveGroupResp, error) {
|
||||
msgCount, groupCount, groups, dateCount, err := m.MsgDatabase.RangeGroupSendCount(
|
||||
ctx,
|
||||
time.UnixMilli(req.Start),
|
||||
time.UnixMilli(req.End),
|
||||
req.Ase,
|
||||
req.Pagination.PageNumber,
|
||||
req.Pagination.ShowNumber,
|
||||
)
|
||||
msgCount, groupCount, groups, dateCount, err := m.MsgDatabase.RangeGroupSendCount(ctx, time.UnixMilli(req.Start), time.UnixMilli(req.End), req.Ase, req.Pagination.PageNumber, req.Pagination.ShowNumber)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var pbgroups []*msg.ActiveGroup
|
||||
if len(groups) > 0 {
|
||||
groupIDs := utils.Slice(groups, func(e *unrelation.GroupCount) string { return e.GroupID })
|
||||
groupIDs := datautil.Slice(groups, func(e *relation.GroupCount) string { return e.GroupID })
|
||||
resp, err := m.GroupLocalCache.GetGroupInfos(ctx, groupIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -16,20 +16,19 @@ package msg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/util/conversationutil"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
"github.com/openimsdk/tools/utils/timeutil"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/OpenIMSDK/protocol/msg"
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
"github.com/openimsdk/protocol/msg"
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
"github.com/openimsdk/tools/log"
|
||||
)
|
||||
|
||||
func (m *msgServer) PullMessageBySeqs(
|
||||
ctx context.Context,
|
||||
req *sdkws.PullMessageBySeqsReq,
|
||||
) (*sdkws.PullMessageBySeqsResp, error) {
|
||||
func (m *msgServer) PullMessageBySeqs(ctx context.Context, req *sdkws.PullMessageBySeqsReq) (*sdkws.PullMessageBySeqsResp, error) {
|
||||
resp := &sdkws.PullMessageBySeqsResp{}
|
||||
resp.Msgs = make(map[string]*sdkws.PullMsgs)
|
||||
resp.NotificationMsgs = make(map[string]*sdkws.PullMsgs)
|
||||
@@ -88,7 +87,7 @@ func (m *msgServer) PullMessageBySeqs(
|
||||
}
|
||||
|
||||
func (m *msgServer) GetMaxSeq(ctx context.Context, req *sdkws.GetMaxSeqReq) (*sdkws.GetMaxSeqResp, error) {
|
||||
if err := authverify.CheckAccessV3(ctx, req.UserID, m.config); err != nil {
|
||||
if err := authverify.CheckAccessV3(ctx, req.UserID, m.config.Share.IMAdminUserID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conversationIDs, err := m.ConversationLocalCache.GetConversationIDs(ctx, req.UserID)
|
||||
@@ -96,9 +95,9 @@ func (m *msgServer) GetMaxSeq(ctx context.Context, req *sdkws.GetMaxSeqReq) (*sd
|
||||
return nil, err
|
||||
}
|
||||
for _, conversationID := range conversationIDs {
|
||||
conversationIDs = append(conversationIDs, utils.GetNotificationConversationIDByConversationID(conversationID))
|
||||
conversationIDs = append(conversationIDs, conversationutil.GetNotificationConversationIDByConversationID(conversationID))
|
||||
}
|
||||
conversationIDs = append(conversationIDs, utils.GetSelfNotificationConversationID(req.UserID))
|
||||
conversationIDs = append(conversationIDs, conversationutil.GetSelfNotificationConversationID(req.UserID))
|
||||
log.ZDebug(ctx, "GetMaxSeq", "conversationIDs", conversationIDs)
|
||||
maxSeqs, err := m.MsgDatabase.GetMaxSeqs(ctx, conversationIDs)
|
||||
if err != nil {
|
||||
@@ -133,7 +132,7 @@ func (m *msgServer) SearchMessage(ctx context.Context, req *msg.SearchMessageReq
|
||||
switch chatLog.SessionType {
|
||||
case constant.SingleChatType, constant.NotificationChatType:
|
||||
recvIDs = append(recvIDs, chatLog.RecvID)
|
||||
case constant.GroupChatType, constant.SuperGroupChatType:
|
||||
case constant.WriteGroupChatType, constant.ReadGroupChatType:
|
||||
groupIDs = append(groupIDs, chatLog.GroupID)
|
||||
}
|
||||
}
|
||||
@@ -175,7 +174,7 @@ func (m *msgServer) SearchMessage(ctx context.Context, req *msg.SearchMessageReq
|
||||
// Construct response with updated information
|
||||
for _, chatLog := range chatLogs {
|
||||
pbchatLog := &msg.ChatLog{}
|
||||
utils.CopyStructFields(pbchatLog, chatLog)
|
||||
datautil.CopyStructFields(pbchatLog, chatLog)
|
||||
pbchatLog.SendTime = chatLog.SendTime
|
||||
pbchatLog.CreateTime = chatLog.CreateTime
|
||||
if chatLog.SenderNickname == "" {
|
||||
@@ -184,7 +183,7 @@ func (m *msgServer) SearchMessage(ctx context.Context, req *msg.SearchMessageReq
|
||||
switch chatLog.SessionType {
|
||||
case constant.SingleChatType, constant.NotificationChatType:
|
||||
pbchatLog.RecvNickname = recvMap[chatLog.RecvID]
|
||||
case constant.GroupChatType, constant.SuperGroupChatType:
|
||||
case constant.WriteGroupChatType, constant.ReadGroupChatType:
|
||||
groupInfo := groupMap[chatLog.GroupID]
|
||||
pbchatLog.SenderFaceURL = groupInfo.FaceURL
|
||||
pbchatLog.GroupMemberCount = groupInfo.MemberCount // Reflects actual member count
|
||||
@@ -200,5 +199,5 @@ func (m *msgServer) SearchMessage(ctx context.Context, req *msg.SearchMessageReq
|
||||
}
|
||||
|
||||
func (m *msgServer) GetServerTime(ctx context.Context, _ *msg.GetServerTimeReq) (*msg.GetServerTimeResp, error) {
|
||||
return &msg.GetServerTimeResp{ServerTime: utils.GetCurrentTimestampByMill()}, nil
|
||||
return &msg.GetServerTimeResp{ServerTime: timeutil.GetCurrentTimestampByMill()}, nil
|
||||
}
|
||||
|
||||
@@ -15,34 +15,13 @@
|
||||
package msg
|
||||
|
||||
import (
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
)
|
||||
|
||||
func isMessageHasReadEnabled(msgData *sdkws.MsgData, config *config.GlobalConfig) bool {
|
||||
switch {
|
||||
case msgData.ContentType == constant.HasReadReceipt && msgData.SessionType == constant.SingleChatType:
|
||||
if config.SingleMessageHasReadReceiptEnable {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case msgData.ContentType == constant.HasReadReceipt && msgData.SessionType == constant.SuperGroupChatType:
|
||||
if config.GroupMessageHasReadReceiptEnable {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func IsNotFound(err error) bool {
|
||||
switch utils.Unwrap(err) {
|
||||
switch errs.Unwrap(err) {
|
||||
case redis.Nil, mongo.ErrNoDocuments:
|
||||
return true
|
||||
default:
|
||||
|
||||
+40
-50
@@ -16,16 +16,18 @@ package msg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
"github.com/openimsdk/tools/utils/encrypt"
|
||||
"github.com/openimsdk/tools/utils/timeutil"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/OpenIMSDK/protocol/msg"
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
"github.com/openimsdk/protocol/msg"
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
)
|
||||
|
||||
var ExcludeContentType = []int{constant.HasReadReceipt}
|
||||
@@ -50,10 +52,7 @@ type MessageRevoked struct {
|
||||
func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgReq) error {
|
||||
switch data.MsgData.SessionType {
|
||||
case constant.SingleChatType:
|
||||
if len(m.config.Manager.UserID) > 0 && utils.IsContain(data.MsgData.SendID, m.config.Manager.UserID) {
|
||||
return nil
|
||||
}
|
||||
if utils.IsContain(data.MsgData.SendID, m.config.IMAdmin.UserID) {
|
||||
if datautil.Contain(data.MsgData.SendID, m.config.Share.IMAdminUserID...) {
|
||||
return nil
|
||||
}
|
||||
if data.MsgData.ContentType <= constant.NotificationEnd &&
|
||||
@@ -65,35 +64,33 @@ func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgRe
|
||||
return err
|
||||
}
|
||||
if black {
|
||||
return errs.ErrBlockedByPeer.Wrap()
|
||||
return servererrs.ErrBlockedByPeer.Wrap()
|
||||
}
|
||||
if m.config.MessageVerify.FriendVerify != nil && *m.config.MessageVerify.FriendVerify {
|
||||
if m.config.RpcConfig.FriendVerify {
|
||||
friend, err := m.FriendLocalCache.IsFriend(ctx, data.MsgData.SendID, data.MsgData.RecvID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !friend {
|
||||
return errs.ErrNotPeersFriend.Wrap()
|
||||
return servererrs.ErrNotPeersFriend.Wrap()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
case constant.SuperGroupChatType:
|
||||
case constant.ReadGroupChatType:
|
||||
groupInfo, err := m.GroupLocalCache.GetGroupInfo(ctx, data.MsgData.GroupID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if groupInfo.Status == constant.GroupStatusDismissed &&
|
||||
data.MsgData.ContentType != constant.GroupDismissedNotification {
|
||||
return errs.ErrDismissedAlready.Wrap()
|
||||
return servererrs.ErrDismissedAlready.Wrap()
|
||||
}
|
||||
if groupInfo.GroupType == constant.SuperGroup {
|
||||
return nil
|
||||
}
|
||||
if len(m.config.Manager.UserID) > 0 && utils.IsContain(data.MsgData.SendID, m.config.Manager.UserID) {
|
||||
return nil
|
||||
}
|
||||
if utils.IsContain(data.MsgData.SendID, m.config.IMAdmin.UserID) {
|
||||
|
||||
if datautil.Contain(data.MsgData.SendID, m.config.Share.IMAdminUserID...) {
|
||||
return nil
|
||||
}
|
||||
if data.MsgData.ContentType <= constant.NotificationEnd &&
|
||||
@@ -105,13 +102,13 @@ func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgRe
|
||||
return err
|
||||
}
|
||||
if _, ok := memberIDs[data.MsgData.SendID]; !ok {
|
||||
return errs.ErrNotInGroupYet.Wrap()
|
||||
return servererrs.ErrNotInGroupYet.Wrap()
|
||||
}
|
||||
|
||||
groupMemberInfo, err := m.GroupLocalCache.GetGroupMember(ctx, data.MsgData.GroupID, data.MsgData.SendID)
|
||||
if err != nil {
|
||||
if errs.ErrRecordNotFound.Is(err) {
|
||||
return errs.ErrNotInGroupYet.Wrap(err.Error())
|
||||
return servererrs.ErrNotInGroupYet.WrapMsg(err.Error())
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -119,10 +116,10 @@ func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgRe
|
||||
return nil
|
||||
} else {
|
||||
if groupMemberInfo.MuteEndTime >= time.Now().UnixMilli() {
|
||||
return errs.ErrMutedInGroup.Wrap()
|
||||
return servererrs.ErrMutedInGroup.Wrap()
|
||||
}
|
||||
if groupInfo.Status == constant.GroupStatusMuted && groupMemberInfo.RoleLevel != constant.GroupAdmin {
|
||||
return errs.ErrMutedGroup.Wrap()
|
||||
return servererrs.ErrMutedGroup.Wrap()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -134,7 +131,7 @@ func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgRe
|
||||
func (m *msgServer) encapsulateMsgData(msg *sdkws.MsgData) {
|
||||
msg.ServerMsgID = GetMsgID(msg.SendID)
|
||||
if msg.SendTime == 0 {
|
||||
msg.SendTime = utils.GetCurrentTimestampByMill()
|
||||
msg.SendTime = timeutil.GetCurrentTimestampByMill()
|
||||
}
|
||||
switch msg.ContentType {
|
||||
case constant.Text:
|
||||
@@ -159,36 +156,30 @@ func (m *msgServer) encapsulateMsgData(msg *sdkws.MsgData) {
|
||||
fallthrough
|
||||
case constant.Quote:
|
||||
case constant.Revoke:
|
||||
utils.SetSwitchFromOptions(msg.Options, constant.IsUnreadCount, false)
|
||||
utils.SetSwitchFromOptions(msg.Options, constant.IsOfflinePush, false)
|
||||
datautil.SetSwitchFromOptions(msg.Options, constant.IsUnreadCount, false)
|
||||
datautil.SetSwitchFromOptions(msg.Options, constant.IsOfflinePush, false)
|
||||
case constant.HasReadReceipt:
|
||||
utils.SetSwitchFromOptions(msg.Options, constant.IsConversationUpdate, false)
|
||||
utils.SetSwitchFromOptions(msg.Options, constant.IsSenderConversationUpdate, false)
|
||||
utils.SetSwitchFromOptions(msg.Options, constant.IsUnreadCount, false)
|
||||
utils.SetSwitchFromOptions(msg.Options, constant.IsOfflinePush, false)
|
||||
datautil.SetSwitchFromOptions(msg.Options, constant.IsConversationUpdate, false)
|
||||
datautil.SetSwitchFromOptions(msg.Options, constant.IsSenderConversationUpdate, false)
|
||||
datautil.SetSwitchFromOptions(msg.Options, constant.IsUnreadCount, false)
|
||||
datautil.SetSwitchFromOptions(msg.Options, constant.IsOfflinePush, false)
|
||||
case constant.Typing:
|
||||
utils.SetSwitchFromOptions(msg.Options, constant.IsHistory, false)
|
||||
utils.SetSwitchFromOptions(msg.Options, constant.IsPersistent, false)
|
||||
utils.SetSwitchFromOptions(msg.Options, constant.IsSenderSync, false)
|
||||
utils.SetSwitchFromOptions(msg.Options, constant.IsConversationUpdate, false)
|
||||
utils.SetSwitchFromOptions(msg.Options, constant.IsSenderConversationUpdate, false)
|
||||
utils.SetSwitchFromOptions(msg.Options, constant.IsUnreadCount, false)
|
||||
utils.SetSwitchFromOptions(msg.Options, constant.IsOfflinePush, false)
|
||||
datautil.SetSwitchFromOptions(msg.Options, constant.IsHistory, false)
|
||||
datautil.SetSwitchFromOptions(msg.Options, constant.IsPersistent, false)
|
||||
datautil.SetSwitchFromOptions(msg.Options, constant.IsSenderSync, false)
|
||||
datautil.SetSwitchFromOptions(msg.Options, constant.IsConversationUpdate, false)
|
||||
datautil.SetSwitchFromOptions(msg.Options, constant.IsSenderConversationUpdate, false)
|
||||
datautil.SetSwitchFromOptions(msg.Options, constant.IsUnreadCount, false)
|
||||
datautil.SetSwitchFromOptions(msg.Options, constant.IsOfflinePush, false)
|
||||
}
|
||||
}
|
||||
|
||||
func GetMsgID(sendID string) string {
|
||||
t := time.Now().Format("2006-01-02 15:04:05")
|
||||
return utils.Md5(t + "-" + sendID + "-" + strconv.Itoa(rand.Int()))
|
||||
t := timeutil.GetCurrentTimeFormatted()
|
||||
return encrypt.Md5(t + "-" + sendID + "-" + strconv.Itoa(rand.Int()))
|
||||
}
|
||||
|
||||
func (m *msgServer) modifyMessageByUserMessageReceiveOpt(
|
||||
ctx context.Context,
|
||||
userID, conversationID string,
|
||||
sessionType int,
|
||||
pb *msg.SendMsgReq,
|
||||
) (bool, error) {
|
||||
defer log.ZDebug(ctx, "modifyMessageByUserMessageReceiveOpt return")
|
||||
func (m *msgServer) modifyMessageByUserMessageReceiveOpt(ctx context.Context, userID, conversationID string, sessionType int, pb *msg.SendMsgReq) (bool, error) {
|
||||
opt, err := m.UserLocalCache.GetUserGlobalMsgRecvOpt(ctx, userID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
@@ -201,10 +192,9 @@ func (m *msgServer) modifyMessageByUserMessageReceiveOpt(
|
||||
if pb.MsgData.Options == nil {
|
||||
pb.MsgData.Options = make(map[string]bool, 10)
|
||||
}
|
||||
utils.SetSwitchFromOptions(pb.MsgData.Options, constant.IsOfflinePush, false)
|
||||
datautil.SetSwitchFromOptions(pb.MsgData.Options, constant.IsOfflinePush, false)
|
||||
return true, nil
|
||||
}
|
||||
// conversationID := utils.GetConversationIDBySessionType(conversationID, sessionType)
|
||||
singleOpt, err := m.ConversationLocalCache.GetSingleConversationRecvMsgOpt(ctx, userID, conversationID)
|
||||
if errs.ErrRecordNotFound.Is(err) {
|
||||
return true, nil
|
||||
@@ -215,7 +205,7 @@ func (m *msgServer) modifyMessageByUserMessageReceiveOpt(
|
||||
case constant.ReceiveMessage:
|
||||
return true, nil
|
||||
case constant.NotReceiveMessage:
|
||||
if utils.IsContainInt(int(pb.MsgData.ContentType), ExcludeContentType) {
|
||||
if datautil.Contain(int(pb.MsgData.ContentType), ExcludeContentType...) {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
@@ -223,7 +213,7 @@ func (m *msgServer) modifyMessageByUserMessageReceiveOpt(
|
||||
if pb.MsgData.Options == nil {
|
||||
pb.MsgData.Options = make(map[string]bool, 10)
|
||||
}
|
||||
utils.SetSwitchFromOptions(pb.MsgData.Options, constant.IsOfflinePush, false)
|
||||
datautil.SetSwitchFromOptions(pb.MsgData.Options, constant.IsOfflinePush, false)
|
||||
return true, nil
|
||||
}
|
||||
return true, nil
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package statistics
|
||||
+17
-17
@@ -17,16 +17,16 @@ package third
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/OpenIMSDK/protocol/third"
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
utils2 "github.com/OpenIMSDK/tools/utils"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
|
||||
relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
"github.com/openimsdk/protocol/third"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
"github.com/openimsdk/tools/utils/stringutil"
|
||||
)
|
||||
|
||||
func genLogID() string {
|
||||
@@ -45,7 +45,7 @@ func genLogID() string {
|
||||
}
|
||||
|
||||
func (t *thirdServer) UploadLogs(ctx context.Context, req *third.UploadLogsReq) (*third.UploadLogsResp, error) {
|
||||
var DBlogs []*relationtb.LogModel
|
||||
var dbLogs []*relationtb.LogModel
|
||||
userID := ctx.Value(constant.OpUserID).(string)
|
||||
platform := constant.PlatformID2Name[int(req.Platform)]
|
||||
for _, fileURL := range req.FileURLs {
|
||||
@@ -70,11 +70,11 @@ func (t *thirdServer) UploadLogs(ctx context.Context, req *third.UploadLogsReq)
|
||||
}
|
||||
}
|
||||
if log.LogID == "" {
|
||||
return nil, errs.ErrData.Wrap("LogModel id gen error")
|
||||
return nil, servererrs.ErrData.WrapMsg("LogModel id gen error")
|
||||
}
|
||||
DBlogs = append(DBlogs, &log)
|
||||
dbLogs = append(dbLogs, &log)
|
||||
}
|
||||
err := t.thirdDatabase.UploadLogs(ctx, DBlogs)
|
||||
err := t.thirdDatabase.UploadLogs(ctx, dbLogs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -82,7 +82,7 @@ func (t *thirdServer) UploadLogs(ctx context.Context, req *third.UploadLogsReq)
|
||||
}
|
||||
|
||||
func (t *thirdServer) DeleteLogs(ctx context.Context, req *third.DeleteLogsReq) (*third.DeleteLogsResp, error) {
|
||||
if err := authverify.CheckAdmin(ctx, t.config); err != nil {
|
||||
if err := authverify.CheckAdmin(ctx, t.config.Share.IMAdminUserID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userID := ""
|
||||
@@ -94,8 +94,8 @@ func (t *thirdServer) DeleteLogs(ctx context.Context, req *third.DeleteLogsReq)
|
||||
for _, log := range logs {
|
||||
logIDs = append(logIDs, log.LogID)
|
||||
}
|
||||
if ids := utils2.Single(req.LogIDs, logIDs); len(ids) > 0 {
|
||||
return nil, errs.ErrRecordNotFound.Wrap(fmt.Sprintf("logIDs not found%#v", ids))
|
||||
if ids := datautil.Single(req.LogIDs, logIDs); len(ids) > 0 {
|
||||
return nil, errs.ErrRecordNotFound.WrapMsg("logIDs not found", "logIDs", ids)
|
||||
}
|
||||
err = t.thirdDatabase.DeleteLogs(ctx, req.LogIDs, userID)
|
||||
if err != nil {
|
||||
@@ -110,7 +110,7 @@ func dbToPbLogInfos(logs []*relationtb.LogModel) []*third.LogInfo {
|
||||
return &third.LogInfo{
|
||||
Filename: log.FileName,
|
||||
UserID: log.UserID,
|
||||
Platform: utils.StringToInt32(log.Platform),
|
||||
Platform: stringutil.StringToInt32(log.Platform),
|
||||
Url: log.Url,
|
||||
CreateTime: log.CreateTime.UnixMilli(),
|
||||
LogID: log.LogID,
|
||||
@@ -119,11 +119,11 @@ func dbToPbLogInfos(logs []*relationtb.LogModel) []*third.LogInfo {
|
||||
Ex: log.Ex,
|
||||
}
|
||||
}
|
||||
return utils.Slice(logs, db2pbForLogInfo)
|
||||
return datautil.Slice(logs, db2pbForLogInfo)
|
||||
}
|
||||
|
||||
func (t *thirdServer) SearchLogs(ctx context.Context, req *third.SearchLogsReq) (*third.SearchLogsResp, error) {
|
||||
if err := authverify.CheckAdmin(ctx, t.config); err != nil {
|
||||
if err := authverify.CheckAdmin(ctx, t.config.Share.IMAdminUserID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var (
|
||||
@@ -131,7 +131,7 @@ func (t *thirdServer) SearchLogs(ctx context.Context, req *third.SearchLogsReq)
|
||||
userIDs []string
|
||||
)
|
||||
if req.StartTime > req.EndTime {
|
||||
return nil, errs.ErrArgs.Wrap("startTime>endTime")
|
||||
return nil, errs.ErrArgs.WrapMsg("startTime>endTime")
|
||||
}
|
||||
if req.StartTime == 0 && req.EndTime == 0 {
|
||||
t := time.Date(2019, time.January, 1, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
+24
-26
@@ -23,15 +23,16 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/third"
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/OpenIMSDK/tools/mcontext"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
"github.com/google/uuid"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/s3"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/s3/cont"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
|
||||
"github.com/openimsdk/protocol/third"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/mcontext"
|
||||
"github.com/openimsdk/tools/s3"
|
||||
"github.com/openimsdk/tools/s3/cont"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
)
|
||||
|
||||
func (t *thirdServer) PartLimit(ctx context.Context, req *third.PartLimitReq) (*third.PartLimitResp, error) {
|
||||
@@ -52,7 +53,6 @@ func (t *thirdServer) PartSize(ctx context.Context, req *third.PartSizeReq) (*th
|
||||
}
|
||||
|
||||
func (t *thirdServer) InitiateMultipartUpload(ctx context.Context, req *third.InitiateMultipartUploadReq) (*third.InitiateMultipartUploadResp, error) {
|
||||
defer log.ZDebug(ctx, "return")
|
||||
if err := t.checkUploadName(ctx, req.Name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -74,7 +74,7 @@ func (t *thirdServer) InitiateMultipartUpload(ctx context.Context, req *third.In
|
||||
return nil, err
|
||||
}
|
||||
return &third.InitiateMultipartUploadResp{
|
||||
Url: t.apiAddress(obj.Name),
|
||||
Url: t.apiAddress(req.UrlPrefix, obj.Name),
|
||||
}, nil
|
||||
}
|
||||
return nil, err
|
||||
@@ -107,8 +107,7 @@ func (t *thirdServer) InitiateMultipartUpload(ctx context.Context, req *third.In
|
||||
}
|
||||
|
||||
func (t *thirdServer) AuthSign(ctx context.Context, req *third.AuthSignReq) (*third.AuthSignResp, error) {
|
||||
defer log.ZDebug(ctx, "return")
|
||||
partNumbers := utils.Slice(req.PartNumbers, func(partNumber int32) int { return int(partNumber) })
|
||||
partNumbers := datautil.Slice(req.PartNumbers, func(partNumber int32) int { return int(partNumber) })
|
||||
result, err := t.s3dataBase.AuthSign(ctx, req.UploadID, partNumbers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -131,7 +130,6 @@ func (t *thirdServer) AuthSign(ctx context.Context, req *third.AuthSignReq) (*th
|
||||
}
|
||||
|
||||
func (t *thirdServer) CompleteMultipartUpload(ctx context.Context, req *third.CompleteMultipartUploadReq) (*third.CompleteMultipartUploadResp, error) {
|
||||
defer log.ZDebug(ctx, "return")
|
||||
if err := t.checkUploadName(ctx, req.Name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -153,7 +151,7 @@ func (t *thirdServer) CompleteMultipartUpload(ctx context.Context, req *third.Co
|
||||
return nil, err
|
||||
}
|
||||
return &third.CompleteMultipartUploadResp{
|
||||
Url: t.apiAddress(obj.Name),
|
||||
Url: t.apiAddress(req.UrlPrefix, obj.Name),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -169,7 +167,7 @@ func (t *thirdServer) AccessURL(ctx context.Context, req *third.AccessURLReq) (*
|
||||
opt.Image.Height, _ = strconv.Atoi(req.Query["height"])
|
||||
log.ZDebug(ctx, "AccessURL image", "name", req.Name, "option", opt.Image)
|
||||
default:
|
||||
return nil, errs.ErrArgs.Wrap("invalid query type")
|
||||
return nil, errs.ErrArgs.WrapMsg("invalid query type")
|
||||
}
|
||||
}
|
||||
expireTime, rawURL, err := t.s3dataBase.AccessURL(ctx, req.Name, t.defaultExpire, opt)
|
||||
@@ -184,10 +182,10 @@ func (t *thirdServer) AccessURL(ctx context.Context, req *third.AccessURLReq) (*
|
||||
|
||||
func (t *thirdServer) InitiateFormData(ctx context.Context, req *third.InitiateFormDataReq) (*third.InitiateFormDataResp, error) {
|
||||
if req.Name == "" {
|
||||
return nil, errs.ErrArgs.Wrap("name is empty")
|
||||
return nil, errs.ErrArgs.WrapMsg("name is empty")
|
||||
}
|
||||
if req.Size <= 0 {
|
||||
return nil, errs.ErrArgs.Wrap("size must be greater than 0")
|
||||
return nil, errs.ErrArgs.WrapMsg("size must be greater than 0")
|
||||
}
|
||||
if err := t.checkUploadName(ctx, req.Name); err != nil {
|
||||
return nil, err
|
||||
@@ -209,7 +207,7 @@ func (t *thirdServer) InitiateFormData(ctx context.Context, req *third.InitiateF
|
||||
}
|
||||
uid, err := uuid.NewRandom()
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err, "uuid NewRandom failed")
|
||||
return nil, errs.WrapMsg(err, "uuid NewRandom failed")
|
||||
}
|
||||
if key == "" {
|
||||
date := time.Now().Format("20060102")
|
||||
@@ -224,7 +222,7 @@ func (t *thirdServer) InitiateFormData(ctx context.Context, req *third.InitiateF
|
||||
}
|
||||
mateData, err := json.Marshal(&mate)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err, "marshal failed")
|
||||
return nil, errs.WrapMsg(err, "marshal failed")
|
||||
}
|
||||
resp, err := t.s3dataBase.FormData(ctx, key, req.Size, req.ContentType, duration)
|
||||
if err != nil {
|
||||
@@ -237,7 +235,7 @@ func (t *thirdServer) InitiateFormData(ctx context.Context, req *third.InitiateF
|
||||
Header: toPbMapArray(resp.Header),
|
||||
FormData: resp.FormData,
|
||||
Expires: resp.Expires.UnixMilli(),
|
||||
SuccessCodes: utils.Slice(resp.SuccessCodes, func(code int) int32 {
|
||||
SuccessCodes: datautil.Slice(resp.SuccessCodes, func(code int) int32 {
|
||||
return int32(code)
|
||||
}),
|
||||
}, nil
|
||||
@@ -245,15 +243,15 @@ func (t *thirdServer) InitiateFormData(ctx context.Context, req *third.InitiateF
|
||||
|
||||
func (t *thirdServer) CompleteFormData(ctx context.Context, req *third.CompleteFormDataReq) (*third.CompleteFormDataResp, error) {
|
||||
if req.Id == "" {
|
||||
return nil, errs.ErrArgs.Wrap("id is empty")
|
||||
return nil, errs.ErrArgs.WrapMsg("id is empty")
|
||||
}
|
||||
data, err := base64.RawStdEncoding.DecodeString(req.Id)
|
||||
if err != nil {
|
||||
return nil, errs.ErrArgs.Wrap("invalid id " + err.Error())
|
||||
return nil, errs.ErrArgs.WrapMsg("invalid id " + err.Error())
|
||||
}
|
||||
var mate FormDataMate
|
||||
if err := json.Unmarshal(data, &mate); err != nil {
|
||||
return nil, errs.ErrArgs.Wrap("invalid id " + err.Error())
|
||||
return nil, errs.ErrArgs.WrapMsg("invalid id " + err.Error())
|
||||
}
|
||||
if err := t.checkUploadName(ctx, mate.Name); err != nil {
|
||||
return nil, err
|
||||
@@ -263,7 +261,7 @@ func (t *thirdServer) CompleteFormData(ctx context.Context, req *third.CompleteF
|
||||
return nil, err
|
||||
}
|
||||
if info.Size > 0 && info.Size != mate.Size {
|
||||
return nil, errs.ErrData.Wrap("file size mismatch")
|
||||
return nil, servererrs.ErrData.WrapMsg("file size mismatch")
|
||||
}
|
||||
obj := &relation.ObjectModel{
|
||||
Name: mate.Name,
|
||||
@@ -278,11 +276,11 @@ func (t *thirdServer) CompleteFormData(ctx context.Context, req *third.CompleteF
|
||||
if err := t.s3dataBase.SetObject(ctx, obj); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &third.CompleteFormDataResp{Url: t.apiAddress(mate.Name)}, nil
|
||||
return &third.CompleteFormDataResp{Url: t.apiAddress(req.UrlPrefix, mate.Name)}, nil
|
||||
}
|
||||
|
||||
func (t *thirdServer) apiAddress(name string) string {
|
||||
return t.apiURL + name
|
||||
func (t *thirdServer) apiAddress(prefix, name string) string {
|
||||
return prefix + name
|
||||
}
|
||||
|
||||
type FormDataMate struct {
|
||||
|
||||
+78
-65
@@ -17,87 +17,100 @@ package third
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/third"
|
||||
"github.com/OpenIMSDK/tools/discoveryregistry"
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/s3"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/s3/cos"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/s3/minio"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/s3/oss"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
"github.com/openimsdk/protocol/third"
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"github.com/openimsdk/tools/db/redisutil"
|
||||
"github.com/openimsdk/tools/discovery"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/s3"
|
||||
"github.com/openimsdk/tools/s3/cos"
|
||||
"github.com/openimsdk/tools/s3/minio"
|
||||
"github.com/openimsdk/tools/s3/oss"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
func Start(config *config.GlobalConfig, client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error {
|
||||
mongo, err := unrelation.NewMongo(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logdb, err := mgo.NewLogMongo(mongo.GetDatabase(config.Mongo.Database))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s3db, err := mgo.NewS3Mongo(mongo.GetDatabase(config.Mongo.Database))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
apiURL := config.Object.ApiURL
|
||||
if apiURL == "" {
|
||||
return errs.Wrap(fmt.Errorf("api is empty"))
|
||||
}
|
||||
if _, err := url.Parse(config.Object.ApiURL); err != nil {
|
||||
return err
|
||||
}
|
||||
if apiURL[len(apiURL)-1] != '/' {
|
||||
apiURL += "/"
|
||||
}
|
||||
apiURL += "object/"
|
||||
rdb, err := cache.NewRedis(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Select the oss method according to the profile policy
|
||||
enable := config.Object.Enable
|
||||
var o s3.Interface
|
||||
switch enable {
|
||||
case "minio":
|
||||
o, err = minio.NewMinio(cache.NewMinioCache(rdb), minio.Config(config.Object.Minio))
|
||||
case "cos":
|
||||
o, err = cos.NewCos(cos.Config(config.Object.Cos))
|
||||
case "oss":
|
||||
o, err = oss.NewOSS(oss.Config(config.Object.Oss))
|
||||
default:
|
||||
err = fmt.Errorf("invalid object enable: %s", enable)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
third.RegisterThirdServer(server, &thirdServer{
|
||||
apiURL: apiURL,
|
||||
thirdDatabase: controller.NewThirdDatabase(cache.NewMsgCacheModel(rdb, config), logdb),
|
||||
userRpcClient: rpcclient.NewUserRpcClient(client, config),
|
||||
s3dataBase: controller.NewS3Database(rdb, o, s3db),
|
||||
defaultExpire: time.Hour * 24 * 7,
|
||||
config: config,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
type thirdServer struct {
|
||||
apiURL string
|
||||
thirdDatabase controller.ThirdDatabase
|
||||
s3dataBase controller.S3Database
|
||||
userRpcClient rpcclient.UserRpcClient
|
||||
defaultExpire time.Duration
|
||||
config *config.GlobalConfig
|
||||
config *Config
|
||||
}
|
||||
type Config struct {
|
||||
RpcConfig config.Third
|
||||
RedisConfig config.Redis
|
||||
MongodbConfig config.Mongo
|
||||
ZookeeperConfig config.ZooKeeper
|
||||
NotificationConfig config.Notification
|
||||
Share config.Share
|
||||
MinioConfig config.Minio
|
||||
LocalCacheConfig config.LocalCache
|
||||
}
|
||||
|
||||
func Start(ctx context.Context, config *Config, client discovery.SvcDiscoveryRegistry, server *grpc.Server) error {
|
||||
mgocli, err := mongoutil.NewMongoDB(ctx, config.MongodbConfig.Build())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rdb, err := redisutil.NewRedisClient(ctx, config.RedisConfig.Build())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logdb, err := mgo.NewLogMongo(mgocli.GetDB())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s3db, err := mgo.NewS3Mongo(mgocli.GetDB())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
apiURL := config.MinioConfig.URL
|
||||
if apiURL == "" {
|
||||
return errs.Wrap(fmt.Errorf("api is empty"))
|
||||
}
|
||||
if _, err := url.Parse(config.MinioConfig.URL); err != nil {
|
||||
return err
|
||||
}
|
||||
if apiURL[len(apiURL)-1] != '/' {
|
||||
apiURL += "/"
|
||||
}
|
||||
apiURL += "object/"
|
||||
|
||||
// Select the oss method according to the profile policy
|
||||
enable := config.RpcConfig.Object.Enable
|
||||
var o s3.Interface
|
||||
switch enable {
|
||||
case "minio":
|
||||
o, err = minio.NewMinio(ctx, cache.NewMinioCache(rdb), *config.MinioConfig.Build())
|
||||
case "cos":
|
||||
o, err = cos.NewCos(*config.RpcConfig.Object.Cos.Build())
|
||||
case "oss":
|
||||
o, err = oss.NewOSS(*config.RpcConfig.Object.Oss.Build())
|
||||
default:
|
||||
err = fmt.Errorf("invalid object enable: %s", enable)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cache.InitLocalCache(&config.LocalCacheConfig)
|
||||
third.RegisterThirdServer(server, &thirdServer{
|
||||
apiURL: apiURL,
|
||||
thirdDatabase: controller.NewThirdDatabase(cache.NewThirdCache(rdb), logdb),
|
||||
userRpcClient: rpcclient.NewUserRpcClient(client, config.Share.RpcRegisterName.User, config.Share.IMAdminUserID),
|
||||
s3dataBase: controller.NewS3Database(rdb, o, s3db),
|
||||
defaultExpire: time.Hour * 24 * 7,
|
||||
config: config,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *thirdServer) FcmUpdateToken(ctx context.Context, req *third.FcmUpdateTokenReq) (resp *third.FcmUpdateTokenResp, err error) {
|
||||
|
||||
+13
-14
@@ -16,15 +16,14 @@ package third
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/third"
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/OpenIMSDK/tools/mcontext"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
|
||||
"github.com/openimsdk/protocol/third"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/mcontext"
|
||||
)
|
||||
|
||||
func toPbMapArray(m map[string][]string) []*third.KeyValues {
|
||||
@@ -43,21 +42,21 @@ func toPbMapArray(m map[string][]string) []*third.KeyValues {
|
||||
|
||||
func (t *thirdServer) checkUploadName(ctx context.Context, name string) error {
|
||||
if name == "" {
|
||||
return errs.ErrArgs.Wrap("name is empty")
|
||||
return errs.ErrArgs.WrapMsg("name is empty")
|
||||
}
|
||||
if name[0] == '/' {
|
||||
return errs.ErrArgs.Wrap("name cannot start with `/`")
|
||||
return errs.ErrArgs.WrapMsg("name cannot start with `/`")
|
||||
}
|
||||
if err := checkValidObjectName(name); err != nil {
|
||||
return errs.ErrArgs.Wrap(err.Error())
|
||||
return errs.ErrArgs.WrapMsg(err.Error())
|
||||
}
|
||||
opUserID := mcontext.GetOpUserID(ctx)
|
||||
if opUserID == "" {
|
||||
return errs.ErrNoPermission.Wrap("opUserID is empty")
|
||||
return errs.ErrNoPermission.WrapMsg("opUserID is empty")
|
||||
}
|
||||
if !authverify.IsManagerUserID(opUserID, t.config) {
|
||||
if !authverify.IsManagerUserID(opUserID, t.config.Share.IMAdminUserID) {
|
||||
if !strings.HasPrefix(name, opUserID+"/") {
|
||||
return errs.ErrNoPermission.Wrap(fmt.Sprintf("name must start with `%s/`", opUserID))
|
||||
return errs.ErrNoPermission.WrapMsg(fmt.Sprintf("name must start with `%s/`", opUserID))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -65,21 +64,21 @@ func (t *thirdServer) checkUploadName(ctx context.Context, name string) error {
|
||||
|
||||
func checkValidObjectNamePrefix(objectName string) error {
|
||||
if len(objectName) > 1024 {
|
||||
return errors.New("object name cannot be longer than 1024 characters")
|
||||
return errs.New("object name cannot be longer than 1024 characters")
|
||||
}
|
||||
if !utf8.ValidString(objectName) {
|
||||
return errors.New("object name with non UTF-8 strings are not supported")
|
||||
return errs.New("object name with non UTF-8 strings are not supported")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkValidObjectName(objectName string) error {
|
||||
if strings.TrimSpace(objectName) == "" {
|
||||
return errors.New("object name cannot be empty")
|
||||
return errs.New("object name cannot be empty")
|
||||
}
|
||||
return checkValidObjectNamePrefix(objectName)
|
||||
}
|
||||
|
||||
func (t *thirdServer) IsManagerUserID(opUserID string) bool {
|
||||
return authverify.IsManagerUserID(opUserID, t.config)
|
||||
return authverify.IsManagerUserID(opUserID, t.config.Share.IMAdminUserID)
|
||||
}
|
||||
|
||||
@@ -16,118 +16,101 @@ package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/webhook"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
|
||||
pbuser "github.com/OpenIMSDK/protocol/user"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/http"
|
||||
pbuser "github.com/openimsdk/protocol/user"
|
||||
)
|
||||
|
||||
func CallbackBeforeUpdateUserInfo(ctx context.Context, globalConfig *config.GlobalConfig, req *pbuser.UpdateUserInfoReq) error {
|
||||
if !globalConfig.Callback.CallbackBeforeUpdateUserInfo.Enable {
|
||||
func (s *userServer) webhookBeforeUpdateUserInfo(ctx context.Context, before *config.BeforeConfig, req *pbuser.UpdateUserInfoReq) error {
|
||||
return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
|
||||
cbReq := &cbapi.CallbackBeforeUpdateUserInfoReq{
|
||||
CallbackCommand: cbapi.CallbackBeforeUpdateUserInfoCommand,
|
||||
UserID: req.UserInfo.UserID,
|
||||
FaceURL: &req.UserInfo.FaceURL,
|
||||
Nickname: &req.UserInfo.Nickname,
|
||||
}
|
||||
resp := &cbapi.CallbackBeforeUpdateUserInfoResp{}
|
||||
if err := s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
datautil.NotNilReplace(&req.UserInfo.FaceURL, resp.FaceURL)
|
||||
datautil.NotNilReplace(&req.UserInfo.Ex, resp.Ex)
|
||||
datautil.NotNilReplace(&req.UserInfo.Nickname, resp.Nickname)
|
||||
return nil
|
||||
}
|
||||
cbReq := &cbapi.CallbackBeforeUpdateUserInfoReq{
|
||||
CallbackCommand: cbapi.CallbackBeforeUpdateUserInfoCommand,
|
||||
UserID: req.UserInfo.UserID,
|
||||
FaceURL: &req.UserInfo.FaceURL,
|
||||
Nickname: &req.UserInfo.Nickname,
|
||||
}
|
||||
resp := &cbapi.CallbackBeforeUpdateUserInfoResp{}
|
||||
if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackBeforeUpdateUserInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
utils.NotNilReplace(&req.UserInfo.FaceURL, resp.FaceURL)
|
||||
utils.NotNilReplace(&req.UserInfo.Ex, resp.Ex)
|
||||
utils.NotNilReplace(&req.UserInfo.Nickname, resp.Nickname)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
func CallbackAfterUpdateUserInfo(ctx context.Context, globalConfig *config.GlobalConfig, req *pbuser.UpdateUserInfoReq) error {
|
||||
if !globalConfig.Callback.CallbackAfterUpdateUserInfo.Enable {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *userServer) webhookAfterUpdateUserInfo(ctx context.Context, after *config.AfterConfig, req *pbuser.UpdateUserInfoReq) {
|
||||
cbReq := &cbapi.CallbackAfterUpdateUserInfoReq{
|
||||
CallbackCommand: cbapi.CallbackAfterUpdateUserInfoCommand,
|
||||
UserID: req.UserInfo.UserID,
|
||||
FaceURL: req.UserInfo.FaceURL,
|
||||
Nickname: req.UserInfo.Nickname,
|
||||
}
|
||||
resp := &cbapi.CallbackAfterUpdateUserInfoResp{}
|
||||
if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackBeforeUpdateUserInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &cbapi.CallbackAfterUpdateUserInfoResp{}, after)
|
||||
}
|
||||
func CallbackBeforeUpdateUserInfoEx(ctx context.Context, globalConfig *config.GlobalConfig, req *pbuser.UpdateUserInfoExReq) error {
|
||||
if !globalConfig.Callback.CallbackBeforeUpdateUserInfoEx.Enable {
|
||||
|
||||
func (s *userServer) webhookBeforeUpdateUserInfoEx(ctx context.Context, before *config.BeforeConfig, req *pbuser.UpdateUserInfoExReq) error {
|
||||
return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
|
||||
cbReq := &cbapi.CallbackBeforeUpdateUserInfoExReq{
|
||||
CallbackCommand: cbapi.CallbackBeforeUpdateUserInfoExCommand,
|
||||
UserID: req.UserInfo.UserID,
|
||||
FaceURL: req.UserInfo.FaceURL,
|
||||
Nickname: req.UserInfo.Nickname,
|
||||
}
|
||||
resp := &cbapi.CallbackBeforeUpdateUserInfoExResp{}
|
||||
if err := s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
datautil.NotNilReplace(req.UserInfo.FaceURL, resp.FaceURL)
|
||||
datautil.NotNilReplace(req.UserInfo.Ex, resp.Ex)
|
||||
datautil.NotNilReplace(req.UserInfo.Nickname, resp.Nickname)
|
||||
return nil
|
||||
}
|
||||
cbReq := &cbapi.CallbackBeforeUpdateUserInfoExReq{
|
||||
CallbackCommand: cbapi.CallbackBeforeUpdateUserInfoExCommand,
|
||||
UserID: req.UserInfo.UserID,
|
||||
FaceURL: req.UserInfo.FaceURL,
|
||||
Nickname: req.UserInfo.Nickname,
|
||||
}
|
||||
resp := &cbapi.CallbackBeforeUpdateUserInfoExResp{}
|
||||
if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackBeforeUpdateUserInfoEx); err != nil {
|
||||
return err
|
||||
}
|
||||
utils.NotNilReplace(req.UserInfo.FaceURL, resp.FaceURL)
|
||||
utils.NotNilReplace(req.UserInfo.Ex, resp.Ex)
|
||||
utils.NotNilReplace(req.UserInfo.Nickname, resp.Nickname)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
func CallbackAfterUpdateUserInfoEx(ctx context.Context, globalConfig *config.GlobalConfig, req *pbuser.UpdateUserInfoExReq) error {
|
||||
if !globalConfig.Callback.CallbackAfterUpdateUserInfoEx.Enable {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *userServer) webhookAfterUpdateUserInfoEx(ctx context.Context, after *config.AfterConfig, req *pbuser.UpdateUserInfoExReq) {
|
||||
cbReq := &cbapi.CallbackAfterUpdateUserInfoExReq{
|
||||
CallbackCommand: cbapi.CallbackAfterUpdateUserInfoExCommand,
|
||||
UserID: req.UserInfo.UserID,
|
||||
FaceURL: req.UserInfo.FaceURL,
|
||||
Nickname: req.UserInfo.Nickname,
|
||||
}
|
||||
resp := &cbapi.CallbackAfterUpdateUserInfoExResp{}
|
||||
if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackBeforeUpdateUserInfoEx); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &cbapi.CallbackAfterUpdateUserInfoExResp{}, after)
|
||||
}
|
||||
|
||||
func CallbackBeforeUserRegister(ctx context.Context, globalConfig *config.GlobalConfig, req *pbuser.UserRegisterReq) error {
|
||||
if !globalConfig.Callback.CallbackBeforeUserRegister.Enable {
|
||||
return nil
|
||||
}
|
||||
cbReq := &cbapi.CallbackBeforeUserRegisterReq{
|
||||
CallbackCommand: cbapi.CallbackBeforeUserRegisterCommand,
|
||||
Secret: req.Secret,
|
||||
Users: req.Users,
|
||||
}
|
||||
func (s *userServer) webhookBeforeUserRegister(ctx context.Context, before *config.BeforeConfig, req *pbuser.UserRegisterReq) error {
|
||||
return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
|
||||
cbReq := &cbapi.CallbackBeforeUserRegisterReq{
|
||||
CallbackCommand: cbapi.CallbackBeforeUserRegisterCommand,
|
||||
Secret: req.Secret,
|
||||
Users: req.Users,
|
||||
}
|
||||
|
||||
resp := &cbapi.CallbackBeforeUserRegisterResp{}
|
||||
if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackBeforeUpdateUserInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(resp.Users) != 0 {
|
||||
req.Users = resp.Users
|
||||
}
|
||||
return nil
|
||||
resp := &cbapi.CallbackBeforeUserRegisterResp{}
|
||||
|
||||
if err := s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(resp.Users) != 0 {
|
||||
req.Users = resp.Users
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func CallbackAfterUserRegister(ctx context.Context, globalConfig *config.GlobalConfig, req *pbuser.UserRegisterReq) error {
|
||||
if !globalConfig.Callback.CallbackAfterUserRegister.Enable {
|
||||
return nil
|
||||
}
|
||||
func (s *userServer) webhookAfterUserRegister(ctx context.Context, after *config.AfterConfig, req *pbuser.UserRegisterReq) {
|
||||
cbReq := &cbapi.CallbackAfterUserRegisterReq{
|
||||
CallbackCommand: cbapi.CallbackAfterUserRegisterCommand,
|
||||
Secret: req.Secret,
|
||||
Users: req.Users,
|
||||
}
|
||||
|
||||
resp := &cbapi.CallbackAfterUserRegisterResp{}
|
||||
if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackAfterUpdateUserInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &cbapi.CallbackAfterUserRegisterResp{}, after)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient/notification"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
|
||||
relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
)
|
||||
|
||||
type UserNotificationSender struct {
|
||||
*rpcclient.NotificationSender
|
||||
getUsersInfo func(ctx context.Context, userIDs []string) ([]notification.CommonUser, error)
|
||||
// db controller
|
||||
db controller.UserDatabase
|
||||
}
|
||||
|
||||
type userNotificationSenderOptions func(*UserNotificationSender)
|
||||
|
||||
func WithUserDB(db controller.UserDatabase) userNotificationSenderOptions {
|
||||
return func(u *UserNotificationSender) {
|
||||
u.db = db
|
||||
}
|
||||
}
|
||||
|
||||
func WithUserFunc(
|
||||
fn func(ctx context.Context, userIDs []string) (users []*relationtb.UserModel, err error),
|
||||
) userNotificationSenderOptions {
|
||||
return func(u *UserNotificationSender) {
|
||||
f := func(ctx context.Context, userIDs []string) (result []notification.CommonUser, err error) {
|
||||
users, err := fn(ctx, userIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, user := range users {
|
||||
result = append(result, user)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
u.getUsersInfo = f
|
||||
}
|
||||
}
|
||||
|
||||
func NewUserNotificationSender(config *Config, msgRpcClient *rpcclient.MessageRpcClient, opts ...userNotificationSenderOptions) *UserNotificationSender {
|
||||
f := &UserNotificationSender{
|
||||
NotificationSender: rpcclient.NewNotificationSender(&config.NotificationConfig, rpcclient.WithRpcClient(msgRpcClient)),
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(f)
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
/* func (u *UserNotificationSender) getUsersInfoMap(
|
||||
ctx context.Context,
|
||||
userIDs []string,
|
||||
) (map[string]*sdkws.UserInfo, error) {
|
||||
users, err := u.getUsersInfo(ctx, userIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make(map[string]*sdkws.UserInfo)
|
||||
for _, user := range users {
|
||||
result[user.GetUserID()] = user.(*sdkws.UserInfo)
|
||||
}
|
||||
return result, nil
|
||||
} */
|
||||
|
||||
/* func (u *UserNotificationSender) getFromToUserNickname(
|
||||
ctx context.Context,
|
||||
fromUserID, toUserID string,
|
||||
) (string, string, error) {
|
||||
users, err := u.getUsersInfoMap(ctx, []string{fromUserID, toUserID})
|
||||
if err != nil {
|
||||
return "", "", nil
|
||||
}
|
||||
return users[fromUserID].Nickname, users[toUserID].Nickname, nil
|
||||
} */
|
||||
|
||||
func (u *UserNotificationSender) UserStatusChangeNotification(
|
||||
ctx context.Context,
|
||||
tips *sdkws.UserStatusChangeTips,
|
||||
) {
|
||||
u.Notification(ctx, tips.FromUserID, tips.ToUserID, constant.UserStatusChangeNotification, tips)
|
||||
}
|
||||
func (u *UserNotificationSender) UserCommandUpdateNotification(
|
||||
ctx context.Context,
|
||||
tips *sdkws.UserCommandUpdateTips,
|
||||
) {
|
||||
u.Notification(ctx, tips.FromUserID, tips.ToUserID, constant.UserCommandUpdateNotification, tips)
|
||||
}
|
||||
func (u *UserNotificationSender) UserCommandAddNotification(
|
||||
ctx context.Context,
|
||||
tips *sdkws.UserCommandAddTips,
|
||||
) {
|
||||
u.Notification(ctx, tips.FromUserID, tips.ToUserID, constant.UserCommandAddNotification, tips)
|
||||
}
|
||||
func (u *UserNotificationSender) UserCommandDeleteNotification(
|
||||
ctx context.Context,
|
||||
tips *sdkws.UserCommandDeleteTips,
|
||||
) {
|
||||
u.Notification(ctx, tips.FromUserID, tips.ToUserID, constant.UserCommandDeleteNotification, tips)
|
||||
}
|
||||
@@ -18,27 +18,24 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
pbuser "github.com/OpenIMSDK/protocol/user"
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
pbuser "github.com/openimsdk/protocol/user"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
)
|
||||
|
||||
func (s *userServer) UserRegisterCount(
|
||||
ctx context.Context,
|
||||
req *pbuser.UserRegisterCountReq,
|
||||
) (*pbuser.UserRegisterCountResp, error) {
|
||||
func (s *userServer) UserRegisterCount(ctx context.Context, req *pbuser.UserRegisterCountReq) (*pbuser.UserRegisterCountResp, error) {
|
||||
if req.Start > req.End {
|
||||
return nil, errs.ErrArgs.Wrap("start > end")
|
||||
return nil, errs.ErrArgs.WrapMsg("start > end")
|
||||
}
|
||||
total, err := s.CountTotal(ctx, nil)
|
||||
total, err := s.db.CountTotal(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
start := time.UnixMilli(req.Start)
|
||||
before, err := s.CountTotal(ctx, &start)
|
||||
before, err := s.db.CountTotal(ctx, &start)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
count, err := s.CountRangeEverydayTotal(ctx, start, time.UnixMilli(req.End))
|
||||
count, err := s.db.CountRangeEverydayTotal(ctx, start, time.UnixMilli(req.End))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+130
-132
@@ -16,91 +16,100 @@ package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/rpc/friend"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/webhook"
|
||||
"github.com/openimsdk/tools/db/redisutil"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
pbuser "github.com/OpenIMSDK/protocol/user"
|
||||
registry "github.com/OpenIMSDK/tools/discoveryregistry"
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/OpenIMSDK/tools/pagination"
|
||||
"github.com/OpenIMSDK/tools/tx"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/convert"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
|
||||
tablerelation "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient/notification"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
pbuser "github.com/openimsdk/protocol/user"
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
registry "github.com/openimsdk/tools/discovery"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type userServer struct {
|
||||
controller.UserDatabase
|
||||
friendNotificationSender *notification.FriendNotificationSender
|
||||
userNotificationSender *notification.UserNotificationSender
|
||||
db controller.UserDatabase
|
||||
friendNotificationSender *friend.FriendNotificationSender
|
||||
userNotificationSender *UserNotificationSender
|
||||
friendRpcClient *rpcclient.FriendRpcClient
|
||||
groupRpcClient *rpcclient.GroupRpcClient
|
||||
RegisterCenter registry.SvcDiscoveryRegistry
|
||||
config *config.GlobalConfig
|
||||
config *Config
|
||||
webhookClient *webhook.Client
|
||||
}
|
||||
|
||||
func (s *userServer) GetGroupOnlineUser(ctx context.Context, req *pbuser.GetGroupOnlineUserReq) (*pbuser.GetGroupOnlineUserResp, error) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
type Config struct {
|
||||
RpcConfig config.User
|
||||
RedisConfig config.Redis
|
||||
MongodbConfig config.Mongo
|
||||
KafkaConfig config.Kafka
|
||||
ZookeeperConfig config.ZooKeeper
|
||||
NotificationConfig config.Notification
|
||||
Share config.Share
|
||||
WebhooksConfig config.Webhooks
|
||||
LocalCacheConfig config.LocalCache
|
||||
}
|
||||
|
||||
func Start(config *config.GlobalConfig, client registry.SvcDiscoveryRegistry, server *grpc.Server) error {
|
||||
rdb, err := cache.NewRedis(config)
|
||||
func Start(ctx context.Context, config *Config, client registry.SvcDiscoveryRegistry, server *grpc.Server) error {
|
||||
mgocli, err := mongoutil.NewMongoDB(ctx, config.MongodbConfig.Build())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mongo, err := unrelation.NewMongo(config)
|
||||
rdb, err := redisutil.NewRedisClient(ctx, config.RedisConfig.Build())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
users := make([]*tablerelation.UserModel, 0)
|
||||
if len(config.IMAdmin.UserID) != len(config.IMAdmin.Nickname) {
|
||||
return errs.Wrap(fmt.Errorf("the count of ImAdmin.UserID is not equal to the count of ImAdmin.Nickname"))
|
||||
|
||||
for _, v := range config.Share.IMAdminUserID {
|
||||
users = append(users, &tablerelation.UserModel{UserID: v, Nickname: v, AppMangerLevel: constant.AppNotificationAdmin})
|
||||
}
|
||||
for k, v := range config.IMAdmin.UserID {
|
||||
users = append(users, &tablerelation.UserModel{UserID: v, Nickname: config.IMAdmin.Nickname[k], AppMangerLevel: constant.AppNotificationAdmin})
|
||||
}
|
||||
userDB, err := mgo.NewUserMongo(mongo.GetDatabase(config.Mongo.Database))
|
||||
userDB, err := mgo.NewUserMongo(mgocli.GetDB())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cache := cache.NewUserCacheRedis(rdb, userDB, cache.GetDefaultOpt())
|
||||
userMongoDB := unrelation.NewUserMongoDriver(mongo.GetDatabase(config.Mongo.Database))
|
||||
database := controller.NewUserDatabase(userDB, cache, tx.NewMongo(mongo.GetClient()), userMongoDB)
|
||||
friendRpcClient := rpcclient.NewFriendRpcClient(client, config)
|
||||
groupRpcClient := rpcclient.NewGroupRpcClient(client, config)
|
||||
msgRpcClient := rpcclient.NewMessageRpcClient(client, config)
|
||||
userCache := cache.NewUserCacheRedis(rdb, &config.LocalCacheConfig, userDB, cache.GetDefaultOpt())
|
||||
userMongoDB := mgo.NewUserMongoDriver(mgocli.GetDB())
|
||||
database := controller.NewUserDatabase(userDB, userCache, mgocli.GetTx(), userMongoDB)
|
||||
friendRpcClient := rpcclient.NewFriendRpcClient(client, config.Share.RpcRegisterName.Friend)
|
||||
groupRpcClient := rpcclient.NewGroupRpcClient(client, config.Share.RpcRegisterName.Group)
|
||||
msgRpcClient := rpcclient.NewMessageRpcClient(client, config.Share.RpcRegisterName.Msg)
|
||||
cache.InitLocalCache(&config.LocalCacheConfig)
|
||||
u := &userServer{
|
||||
UserDatabase: database,
|
||||
db: database,
|
||||
RegisterCenter: client,
|
||||
friendRpcClient: &friendRpcClient,
|
||||
groupRpcClient: &groupRpcClient,
|
||||
friendNotificationSender: notification.NewFriendNotificationSender(config, &msgRpcClient, notification.WithDBFunc(database.FindWithError)),
|
||||
userNotificationSender: notification.NewUserNotificationSender(config, &msgRpcClient, notification.WithUserFunc(database.FindWithError)),
|
||||
friendNotificationSender: friend.NewFriendNotificationSender(&config.NotificationConfig, &msgRpcClient, friend.WithDBFunc(database.FindWithError)),
|
||||
userNotificationSender: NewUserNotificationSender(config, &msgRpcClient, WithUserFunc(database.FindWithError)),
|
||||
config: config,
|
||||
webhookClient: webhook.NewWebhookClient(config.WebhooksConfig.URL),
|
||||
}
|
||||
pbuser.RegisterUserServer(server, u)
|
||||
return u.UserDatabase.InitOnce(context.Background(), users)
|
||||
return u.db.InitOnce(context.Background(), users)
|
||||
}
|
||||
|
||||
func (s *userServer) GetDesignateUsers(ctx context.Context, req *pbuser.GetDesignateUsersReq) (resp *pbuser.GetDesignateUsersResp, err error) {
|
||||
resp = &pbuser.GetDesignateUsersResp{}
|
||||
users, err := s.FindWithError(ctx, req.UserIDs)
|
||||
users, err := s.db.FindWithError(ctx, req.UserIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -108,20 +117,26 @@ func (s *userServer) GetDesignateUsers(ctx context.Context, req *pbuser.GetDesig
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// deprecated:
|
||||
|
||||
//UpdateUserInfo
|
||||
|
||||
func (s *userServer) UpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserInfoReq) (resp *pbuser.UpdateUserInfoResp, err error) {
|
||||
resp = &pbuser.UpdateUserInfoResp{}
|
||||
err = authverify.CheckAccessV3(ctx, req.UserInfo.UserID, s.config)
|
||||
err = authverify.CheckAccessV3(ctx, req.UserInfo.UserID, s.config.Share.IMAdminUserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := CallbackBeforeUpdateUserInfo(ctx, s.config, req); err != nil {
|
||||
|
||||
if err := s.webhookBeforeUpdateUserInfo(ctx, &s.config.WebhooksConfig.BeforeUpdateUserInfo, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data := convert.UserPb2DBMap(req.UserInfo)
|
||||
if err := s.UpdateByMap(ctx, req.UserInfo.UserID, data); err != nil {
|
||||
if err := s.db.UpdateByMap(ctx, req.UserInfo.UserID, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_ = s.friendNotificationSender.UserInfoUpdatedNotification(ctx, req.UserInfo.UserID)
|
||||
s.friendNotificationSender.UserInfoUpdatedNotification(ctx, req.UserInfo.UserID)
|
||||
friends, err := s.friendRpcClient.GetFriendIDs(ctx, req.UserInfo.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -134,9 +149,7 @@ func (s *userServer) UpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserI
|
||||
for _, friendID := range friends {
|
||||
s.friendNotificationSender.FriendInfoUpdatedNotification(ctx, req.UserInfo.UserID, friendID)
|
||||
}
|
||||
if err = CallbackAfterUpdateUserInfo(ctx, s.config, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.webhookAfterUpdateUserInfo(ctx, &s.config.WebhooksConfig.AfterUpdateUserInfo, req)
|
||||
if err = s.groupRpcClient.NotificationUserInfoUpdate(ctx, req.UserInfo.UserID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -144,19 +157,18 @@ func (s *userServer) UpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserI
|
||||
}
|
||||
func (s *userServer) UpdateUserInfoEx(ctx context.Context, req *pbuser.UpdateUserInfoExReq) (resp *pbuser.UpdateUserInfoExResp, err error) {
|
||||
resp = &pbuser.UpdateUserInfoExResp{}
|
||||
err = authverify.CheckAccessV3(ctx, req.UserInfo.UserID, s.config)
|
||||
err = authverify.CheckAccessV3(ctx, req.UserInfo.UserID, s.config.Share.IMAdminUserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = CallbackBeforeUpdateUserInfoEx(ctx, s.config, req); err != nil {
|
||||
if err = s.webhookBeforeUpdateUserInfoEx(ctx, &s.config.WebhooksConfig.BeforeUpdateUserInfoEx, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := convert.UserPb2DBMapEx(req.UserInfo)
|
||||
if err = s.UpdateByMap(ctx, req.UserInfo.UserID, data); err != nil {
|
||||
if err = s.db.UpdateByMap(ctx, req.UserInfo.UserID, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_ = s.friendNotificationSender.UserInfoUpdatedNotification(ctx, req.UserInfo.UserID)
|
||||
s.friendNotificationSender.UserInfoUpdatedNotification(ctx, req.UserInfo.UserID)
|
||||
friends, err := s.friendRpcClient.GetFriendIDs(ctx, req.UserInfo.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -169,9 +181,7 @@ func (s *userServer) UpdateUserInfoEx(ctx context.Context, req *pbuser.UpdateUse
|
||||
for _, friendID := range friends {
|
||||
s.friendNotificationSender.FriendInfoUpdatedNotification(ctx, req.UserInfo.UserID, friendID)
|
||||
}
|
||||
if err := CallbackAfterUpdateUserInfoEx(ctx, s.config, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.webhookAfterUpdateUserInfoEx(ctx, &s.config.WebhooksConfig.AfterUpdateUserInfoEx, req)
|
||||
if err := s.groupRpcClient.NotificationUserInfoUpdate(ctx, req.UserInfo.UserID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -179,12 +189,12 @@ func (s *userServer) UpdateUserInfoEx(ctx context.Context, req *pbuser.UpdateUse
|
||||
}
|
||||
func (s *userServer) SetGlobalRecvMessageOpt(ctx context.Context, req *pbuser.SetGlobalRecvMessageOptReq) (resp *pbuser.SetGlobalRecvMessageOptResp, err error) {
|
||||
resp = &pbuser.SetGlobalRecvMessageOptResp{}
|
||||
if _, err := s.FindWithError(ctx, []string{req.UserID}); err != nil {
|
||||
if _, err := s.db.FindWithError(ctx, []string{req.UserID}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m := make(map[string]any, 1)
|
||||
m["global_recv_msg_opt"] = req.GlobalRecvMsgOpt
|
||||
if err := s.UpdateByMap(ctx, req.UserID, m); err != nil {
|
||||
if err := s.db.UpdateByMap(ctx, req.UserID, m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.friendNotificationSender.UserInfoUpdatedNotification(ctx, req.UserID)
|
||||
@@ -193,14 +203,14 @@ func (s *userServer) SetGlobalRecvMessageOpt(ctx context.Context, req *pbuser.Se
|
||||
|
||||
func (s *userServer) AccountCheck(ctx context.Context, req *pbuser.AccountCheckReq) (resp *pbuser.AccountCheckResp, err error) {
|
||||
resp = &pbuser.AccountCheckResp{}
|
||||
if utils.Duplicate(req.CheckUserIDs) {
|
||||
return nil, errs.ErrArgs.Wrap("userID repeated")
|
||||
if datautil.Duplicate(req.CheckUserIDs) {
|
||||
return nil, errs.ErrArgs.WrapMsg("userID repeated")
|
||||
}
|
||||
err = authverify.CheckAdmin(ctx, s.config)
|
||||
err = authverify.CheckAdmin(ctx, s.config.Share.IMAdminUserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
users, err := s.Find(ctx, req.CheckUserIDs)
|
||||
users, err := s.db.Find(ctx, req.CheckUserIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -222,13 +232,13 @@ func (s *userServer) AccountCheck(ctx context.Context, req *pbuser.AccountCheckR
|
||||
|
||||
func (s *userServer) GetPaginationUsers(ctx context.Context, req *pbuser.GetPaginationUsersReq) (resp *pbuser.GetPaginationUsersResp, err error) {
|
||||
if req.UserID == "" && req.NickName == "" {
|
||||
total, users, err := s.PageFindUser(ctx, constant.IMOrdinaryUser, constant.AppOrdinaryUsers, req.Pagination)
|
||||
total, users, err := s.db.PageFindUser(ctx, constant.IMOrdinaryUser, constant.AppOrdinaryUsers, req.Pagination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pbuser.GetPaginationUsersResp{Total: int32(total), Users: convert.UsersDB2Pb(users)}, err
|
||||
} else {
|
||||
total, users, err := s.PageFindUserWithKeyword(ctx, constant.IMOrdinaryUser, constant.AppOrdinaryUsers, req.UserID, req.NickName, req.Pagination)
|
||||
total, users, err := s.db.PageFindUserWithKeyword(ctx, constant.IMOrdinaryUser, constant.AppOrdinaryUsers, req.UserID, req.NickName, req.Pagination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -241,33 +251,33 @@ func (s *userServer) GetPaginationUsers(ctx context.Context, req *pbuser.GetPagi
|
||||
func (s *userServer) UserRegister(ctx context.Context, req *pbuser.UserRegisterReq) (resp *pbuser.UserRegisterResp, err error) {
|
||||
resp = &pbuser.UserRegisterResp{}
|
||||
if len(req.Users) == 0 {
|
||||
return nil, errs.ErrArgs.Wrap("users is empty")
|
||||
return nil, errs.ErrArgs.WrapMsg("users is empty")
|
||||
}
|
||||
if req.Secret != s.config.Secret {
|
||||
log.ZDebug(ctx, "UserRegister", s.config.Secret, req.Secret)
|
||||
return nil, errs.ErrNoPermission.Wrap("secret invalid")
|
||||
if req.Secret != s.config.Share.Secret {
|
||||
log.ZDebug(ctx, "UserRegister", s.config.Share.Secret, req.Secret)
|
||||
return nil, errs.ErrNoPermission.WrapMsg("secret invalid")
|
||||
}
|
||||
if utils.DuplicateAny(req.Users, func(e *sdkws.UserInfo) string { return e.UserID }) {
|
||||
return nil, errs.ErrArgs.Wrap("userID repeated")
|
||||
if datautil.DuplicateAny(req.Users, func(e *sdkws.UserInfo) string { return e.UserID }) {
|
||||
return nil, errs.ErrArgs.WrapMsg("userID repeated")
|
||||
}
|
||||
userIDs := make([]string, 0)
|
||||
for _, user := range req.Users {
|
||||
if user.UserID == "" {
|
||||
return nil, errs.ErrArgs.Wrap("userID is empty")
|
||||
return nil, errs.ErrArgs.WrapMsg("userID is empty")
|
||||
}
|
||||
if strings.Contains(user.UserID, ":") {
|
||||
return nil, errs.ErrArgs.Wrap("userID contains ':' is invalid userID")
|
||||
return nil, errs.ErrArgs.WrapMsg("userID contains ':' is invalid userID")
|
||||
}
|
||||
userIDs = append(userIDs, user.UserID)
|
||||
}
|
||||
exist, err := s.IsExist(ctx, userIDs)
|
||||
exist, err := s.db.IsExist(ctx, userIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if exist {
|
||||
return nil, errs.ErrRegisteredAlready.Wrap("userID registered already")
|
||||
return nil, servererrs.ErrRegisteredAlready.WrapMsg("userID registered already")
|
||||
}
|
||||
if err := CallbackBeforeUserRegister(ctx, s.config, req); err != nil {
|
||||
if err := s.webhookBeforeUserRegister(ctx, &s.config.WebhooksConfig.BeforeUserRegister, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
now := time.Now()
|
||||
@@ -283,18 +293,16 @@ func (s *userServer) UserRegister(ctx context.Context, req *pbuser.UserRegisterR
|
||||
GlobalRecvMsgOpt: user.GlobalRecvMsgOpt,
|
||||
})
|
||||
}
|
||||
if err := s.Create(ctx, users); err != nil {
|
||||
if err := s.db.Create(ctx, users); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := CallbackAfterUserRegister(ctx, s.config, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.webhookAfterUserRegister(ctx, &s.config.WebhooksConfig.AfterUserRegister, req)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (s *userServer) GetGlobalRecvMessageOpt(ctx context.Context, req *pbuser.GetGlobalRecvMessageOptReq) (resp *pbuser.GetGlobalRecvMessageOptResp, err error) {
|
||||
user, err := s.FindWithError(ctx, []string{req.UserID})
|
||||
user, err := s.db.FindWithError(ctx, []string{req.UserID})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -303,7 +311,7 @@ func (s *userServer) GetGlobalRecvMessageOpt(ctx context.Context, req *pbuser.Ge
|
||||
|
||||
// GetAllUserID Get user account by page.
|
||||
func (s *userServer) GetAllUserID(ctx context.Context, req *pbuser.GetAllUserIDReq) (resp *pbuser.GetAllUserIDResp, err error) {
|
||||
total, userIDs, err := s.UserDatabase.GetAllUserID(ctx, req.Pagination)
|
||||
total, userIDs, err := s.db.GetAllUserID(ctx, req.Pagination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -313,18 +321,18 @@ func (s *userServer) GetAllUserID(ctx context.Context, req *pbuser.GetAllUserIDR
|
||||
// SubscribeOrCancelUsersStatus Subscribe online or cancel online users.
|
||||
func (s *userServer) SubscribeOrCancelUsersStatus(ctx context.Context, req *pbuser.SubscribeOrCancelUsersStatusReq) (resp *pbuser.SubscribeOrCancelUsersStatusResp, err error) {
|
||||
if req.Genre == constant.SubscriberUser {
|
||||
err = s.UserDatabase.SubscribeUsersStatus(ctx, req.UserID, req.UserIDs)
|
||||
err = s.db.SubscribeUsersStatus(ctx, req.UserID, req.UserIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var status []*pbuser.OnlineStatus
|
||||
status, err = s.UserDatabase.GetUserStatus(ctx, req.UserIDs)
|
||||
status, err = s.db.GetUserStatus(ctx, req.UserIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pbuser.SubscribeOrCancelUsersStatusResp{StatusList: status}, nil
|
||||
} else if req.Genre == constant.Unsubscribe {
|
||||
err = s.UserDatabase.UnsubscribeUsersStatus(ctx, req.UserID, req.UserIDs)
|
||||
err = s.db.UnsubscribeUsersStatus(ctx, req.UserID, req.UserIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -335,7 +343,7 @@ func (s *userServer) SubscribeOrCancelUsersStatus(ctx context.Context, req *pbus
|
||||
// GetUserStatus Get the online status of the user.
|
||||
func (s *userServer) GetUserStatus(ctx context.Context, req *pbuser.GetUserStatusReq) (resp *pbuser.GetUserStatusResp,
|
||||
err error) {
|
||||
onlineStatusList, err := s.UserDatabase.GetUserStatus(ctx, req.UserIDs)
|
||||
onlineStatusList, err := s.db.GetUserStatus(ctx, req.UserIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -345,11 +353,11 @@ func (s *userServer) GetUserStatus(ctx context.Context, req *pbuser.GetUserStatu
|
||||
// SetUserStatus Synchronize user's online status.
|
||||
func (s *userServer) SetUserStatus(ctx context.Context, req *pbuser.SetUserStatusReq) (resp *pbuser.SetUserStatusResp,
|
||||
err error) {
|
||||
err = s.UserDatabase.SetUserStatus(ctx, req.UserID, req.Status, req.PlatformID)
|
||||
err = s.db.SetUserStatus(ctx, req.UserID, req.Status, req.PlatformID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list, err := s.UserDatabase.GetSubscribedList(ctx, req.UserID)
|
||||
list, err := s.db.GetSubscribedList(ctx, req.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -369,11 +377,11 @@ func (s *userServer) SetUserStatus(ctx context.Context, req *pbuser.SetUserStatu
|
||||
// GetSubscribeUsersStatus Get the online status of subscribers.
|
||||
func (s *userServer) GetSubscribeUsersStatus(ctx context.Context,
|
||||
req *pbuser.GetSubscribeUsersStatusReq) (*pbuser.GetSubscribeUsersStatusResp, error) {
|
||||
userList, err := s.UserDatabase.GetAllSubscribeList(ctx, req.UserID)
|
||||
userList, err := s.db.GetAllSubscribeList(ctx, req.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
onlineStatusList, err := s.UserDatabase.GetUserStatus(ctx, userList)
|
||||
onlineStatusList, err := s.db.GetUserStatus(ctx, userList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -382,7 +390,7 @@ func (s *userServer) GetSubscribeUsersStatus(ctx context.Context,
|
||||
|
||||
// ProcessUserCommandAdd user general function add.
|
||||
func (s *userServer) ProcessUserCommandAdd(ctx context.Context, req *pbuser.ProcessUserCommandAddReq) (*pbuser.ProcessUserCommandAddResp, error) {
|
||||
err := authverify.CheckAccessV3(ctx, req.UserID, s.config)
|
||||
err := authverify.CheckAccessV3(ctx, req.UserID, s.config.Share.IMAdminUserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -395,8 +403,8 @@ func (s *userServer) ProcessUserCommandAdd(ctx context.Context, req *pbuser.Proc
|
||||
if req.Ex != nil {
|
||||
value = req.Ex.Value
|
||||
}
|
||||
// Assuming you have a method in s.UserDatabase to add a user command
|
||||
err = s.UserDatabase.AddUserCommand(ctx, req.UserID, req.Type, req.Uuid, value, ex)
|
||||
// Assuming you have a method in s.db to add a user command
|
||||
err = s.db.AddUserCommand(ctx, req.UserID, req.Type, req.Uuid, value, ex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -404,21 +412,18 @@ func (s *userServer) ProcessUserCommandAdd(ctx context.Context, req *pbuser.Proc
|
||||
FromUserID: req.UserID,
|
||||
ToUserID: req.UserID,
|
||||
}
|
||||
err = s.userNotificationSender.UserCommandAddNotification(ctx, tips)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.userNotificationSender.UserCommandAddNotification(ctx, tips)
|
||||
return &pbuser.ProcessUserCommandAddResp{}, nil
|
||||
}
|
||||
|
||||
// ProcessUserCommandDelete user general function delete.
|
||||
func (s *userServer) ProcessUserCommandDelete(ctx context.Context, req *pbuser.ProcessUserCommandDeleteReq) (*pbuser.ProcessUserCommandDeleteResp, error) {
|
||||
err := authverify.CheckAccessV3(ctx, req.UserID, s.config)
|
||||
err := authverify.CheckAccessV3(ctx, req.UserID, s.config.Share.IMAdminUserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = s.UserDatabase.DeleteUserCommand(ctx, req.UserID, req.Type, req.Uuid)
|
||||
err = s.db.DeleteUserCommand(ctx, req.UserID, req.Type, req.Uuid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -426,17 +431,13 @@ func (s *userServer) ProcessUserCommandDelete(ctx context.Context, req *pbuser.P
|
||||
FromUserID: req.UserID,
|
||||
ToUserID: req.UserID,
|
||||
}
|
||||
err = s.userNotificationSender.UserCommandDeleteNotification(ctx, tips)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.userNotificationSender.UserCommandDeleteNotification(ctx, tips)
|
||||
return &pbuser.ProcessUserCommandDeleteResp{}, nil
|
||||
}
|
||||
|
||||
// ProcessUserCommandUpdate user general function update.
|
||||
func (s *userServer) ProcessUserCommandUpdate(ctx context.Context, req *pbuser.ProcessUserCommandUpdateReq) (*pbuser.ProcessUserCommandUpdateResp, error) {
|
||||
err := authverify.CheckAccessV3(ctx, req.UserID, s.config)
|
||||
err := authverify.CheckAccessV3(ctx, req.UserID, s.config.Share.IMAdminUserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -450,8 +451,8 @@ func (s *userServer) ProcessUserCommandUpdate(ctx context.Context, req *pbuser.P
|
||||
val["ex"] = req.Ex.Value
|
||||
}
|
||||
|
||||
// Assuming you have a method in s.UserDatabase to update a user command
|
||||
err = s.UserDatabase.UpdateUserCommand(ctx, req.UserID, req.Type, req.Uuid, val)
|
||||
// Assuming you have a method in s.db to update a user command
|
||||
err = s.db.UpdateUserCommand(ctx, req.UserID, req.Type, req.Uuid, val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -459,21 +460,18 @@ func (s *userServer) ProcessUserCommandUpdate(ctx context.Context, req *pbuser.P
|
||||
FromUserID: req.UserID,
|
||||
ToUserID: req.UserID,
|
||||
}
|
||||
err = s.userNotificationSender.UserCommandUpdateNotification(ctx, tips)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.userNotificationSender.UserCommandUpdateNotification(ctx, tips)
|
||||
return &pbuser.ProcessUserCommandUpdateResp{}, nil
|
||||
}
|
||||
|
||||
func (s *userServer) ProcessUserCommandGet(ctx context.Context, req *pbuser.ProcessUserCommandGetReq) (*pbuser.ProcessUserCommandGetResp, error) {
|
||||
|
||||
err := authverify.CheckAccessV3(ctx, req.UserID, s.config)
|
||||
err := authverify.CheckAccessV3(ctx, req.UserID, s.config.Share.IMAdminUserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Fetch user commands from the database
|
||||
commands, err := s.UserDatabase.GetUserCommands(ctx, req.UserID, req.Type)
|
||||
commands, err := s.db.GetUserCommands(ctx, req.UserID, req.Type)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -497,12 +495,12 @@ func (s *userServer) ProcessUserCommandGet(ctx context.Context, req *pbuser.Proc
|
||||
}
|
||||
|
||||
func (s *userServer) ProcessUserCommandGetAll(ctx context.Context, req *pbuser.ProcessUserCommandGetAllReq) (*pbuser.ProcessUserCommandGetAllResp, error) {
|
||||
err := authverify.CheckAccessV3(ctx, req.UserID, s.config)
|
||||
err := authverify.CheckAccessV3(ctx, req.UserID, s.config.Share.IMAdminUserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Fetch user commands from the database
|
||||
commands, err := s.UserDatabase.GetAllUserCommands(ctx, req.UserID)
|
||||
commands, err := s.db.GetAllUserCommands(ctx, req.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -526,14 +524,14 @@ func (s *userServer) ProcessUserCommandGetAll(ctx context.Context, req *pbuser.P
|
||||
}
|
||||
|
||||
func (s *userServer) AddNotificationAccount(ctx context.Context, req *pbuser.AddNotificationAccountReq) (*pbuser.AddNotificationAccountResp, error) {
|
||||
if err := authverify.CheckIMAdmin(ctx, s.config); err != nil {
|
||||
if err := authverify.CheckAdmin(ctx, s.config.Share.IMAdminUserID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if req.UserID == "" {
|
||||
for i := 0; i < 20; i++ {
|
||||
userId := s.genUserID()
|
||||
_, err := s.UserDatabase.FindWithError(ctx, []string{userId})
|
||||
_, err := s.db.FindWithError(ctx, []string{userId})
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
@@ -541,12 +539,12 @@ func (s *userServer) AddNotificationAccount(ctx context.Context, req *pbuser.Add
|
||||
break
|
||||
}
|
||||
if req.UserID == "" {
|
||||
return nil, errs.ErrInternalServer.Wrap("gen user id failed")
|
||||
return nil, errs.ErrInternalServer.WrapMsg("gen user id failed")
|
||||
}
|
||||
} else {
|
||||
_, err := s.UserDatabase.FindWithError(ctx, []string{req.UserID})
|
||||
_, err := s.db.FindWithError(ctx, []string{req.UserID})
|
||||
if err == nil {
|
||||
return nil, errs.ErrArgs.Wrap("userID is used")
|
||||
return nil, errs.ErrArgs.WrapMsg("userID is used")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -557,7 +555,7 @@ func (s *userServer) AddNotificationAccount(ctx context.Context, req *pbuser.Add
|
||||
CreateTime: time.Now(),
|
||||
AppMangerLevel: constant.AppNotificationAdmin,
|
||||
}
|
||||
if err := s.UserDatabase.Create(ctx, []*tablerelation.UserModel{user}); err != nil {
|
||||
if err := s.db.Create(ctx, []*tablerelation.UserModel{user}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -569,11 +567,11 @@ func (s *userServer) AddNotificationAccount(ctx context.Context, req *pbuser.Add
|
||||
}
|
||||
|
||||
func (s *userServer) UpdateNotificationAccountInfo(ctx context.Context, req *pbuser.UpdateNotificationAccountInfoReq) (*pbuser.UpdateNotificationAccountInfoResp, error) {
|
||||
if err := authverify.CheckIMAdmin(ctx, s.config); err != nil {
|
||||
if err := authverify.CheckAdmin(ctx, s.config.Share.IMAdminUserID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := s.UserDatabase.FindWithError(ctx, []string{req.UserID}); err != nil {
|
||||
if _, err := s.db.FindWithError(ctx, []string{req.UserID}); err != nil {
|
||||
return nil, errs.ErrArgs.Wrap()
|
||||
}
|
||||
|
||||
@@ -587,7 +585,7 @@ func (s *userServer) UpdateNotificationAccountInfo(ctx context.Context, req *pbu
|
||||
user["face_url"] = req.FaceURL
|
||||
}
|
||||
|
||||
if err := s.UserDatabase.UpdateByMap(ctx, req.UserID, user); err != nil {
|
||||
if err := s.db.UpdateByMap(ctx, req.UserID, user); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -596,7 +594,7 @@ func (s *userServer) UpdateNotificationAccountInfo(ctx context.Context, req *pbu
|
||||
|
||||
func (s *userServer) SearchNotificationAccount(ctx context.Context, req *pbuser.SearchNotificationAccountReq) (*pbuser.SearchNotificationAccountResp, error) {
|
||||
// Check if user is an admin
|
||||
if err := authverify.CheckIMAdmin(ctx, s.config); err != nil {
|
||||
if err := authverify.CheckAdmin(ctx, s.config.Share.IMAdminUserID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -606,7 +604,7 @@ func (s *userServer) SearchNotificationAccount(ctx context.Context, req *pbuser.
|
||||
// If a keyword is provided in the request
|
||||
if req.Keyword != "" {
|
||||
// Find users by keyword
|
||||
users, err = s.UserDatabase.Find(ctx, []string{req.Keyword})
|
||||
users, err = s.db.Find(ctx, []string{req.Keyword})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -618,7 +616,7 @@ func (s *userServer) SearchNotificationAccount(ctx context.Context, req *pbuser.
|
||||
}
|
||||
|
||||
// Find users by nickname if no users found by keyword
|
||||
users, err = s.UserDatabase.FindByNickname(ctx, req.Keyword)
|
||||
users, err = s.db.FindByNickname(ctx, req.Keyword)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -627,7 +625,7 @@ func (s *userServer) SearchNotificationAccount(ctx context.Context, req *pbuser.
|
||||
}
|
||||
|
||||
// If no keyword, find users with notification settings
|
||||
users, err = s.UserDatabase.FindNotification(ctx, constant.AppNotificationAdmin)
|
||||
users, err = s.db.FindNotification(ctx, constant.AppNotificationAdmin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -638,17 +636,17 @@ func (s *userServer) SearchNotificationAccount(ctx context.Context, req *pbuser.
|
||||
|
||||
func (s *userServer) GetNotificationAccount(ctx context.Context, req *pbuser.GetNotificationAccountReq) (*pbuser.GetNotificationAccountResp, error) {
|
||||
if req.UserID == "" {
|
||||
return nil, errs.ErrArgs.Wrap("userID is empty")
|
||||
return nil, errs.ErrArgs.WrapMsg("userID is empty")
|
||||
}
|
||||
user, err := s.UserDatabase.GetUserByID(ctx, req.UserID)
|
||||
user, err := s.db.GetUserByID(ctx, req.UserID)
|
||||
if err != nil {
|
||||
return nil, errs.ErrUserIDNotFound.Wrap()
|
||||
return nil, servererrs.ErrUserIDNotFound.Wrap()
|
||||
}
|
||||
if user.AppMangerLevel == constant.AppAdmin || user.AppMangerLevel == constant.AppNotificationAdmin {
|
||||
return &pbuser.GetNotificationAccountResp{}, nil
|
||||
}
|
||||
|
||||
return nil, errs.ErrNoPermission.Wrap("notification messages cannot be sent for this ID")
|
||||
return nil, errs.ErrNoPermission.WrapMsg("notification messages cannot be sent for this ID")
|
||||
}
|
||||
|
||||
func (s *userServer) genUserID() string {
|
||||
@@ -670,7 +668,7 @@ func (s *userServer) userModelToResp(users []*relation.UserModel, pagination pag
|
||||
accounts := make([]*pbuser.NotificationAccountInfo, 0)
|
||||
var total int64
|
||||
for _, v := range users {
|
||||
if v.AppMangerLevel == constant.AppNotificationAdmin && !utils.IsContain(v.UserID, s.config.IMAdmin.UserID) {
|
||||
if v.AppMangerLevel == constant.AppNotificationAdmin && !datautil.Contain(v.UserID, s.config.Share.IMAdminUserID...) {
|
||||
temp := &pbuser.NotificationAccountInfo{
|
||||
UserID: v.UserID,
|
||||
FaceURL: v.FaceURL,
|
||||
@@ -681,7 +679,7 @@ func (s *userServer) userModelToResp(users []*relation.UserModel, pagination pag
|
||||
}
|
||||
}
|
||||
|
||||
notificationAccounts := utils.Paginate(accounts, int(pagination.GetPageNumber()), int(pagination.GetShowNumber()))
|
||||
notificationAccounts := datautil.Paginate(accounts, int(pagination.GetPageNumber()), int(pagination.GetShowNumber()))
|
||||
|
||||
return &pbuser.SearchNotificationAccountResp{Total: total, NotificationAccounts: notificationAccounts}
|
||||
}
|
||||
|
||||
@@ -19,14 +19,15 @@ import (
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/OpenIMSDK/tools/mcontext"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/mcontext"
|
||||
"github.com/openimsdk/tools/utils/idutil"
|
||||
"github.com/openimsdk/tools/utils/stringutil"
|
||||
)
|
||||
|
||||
//func (c *MsgTool) ConversationsDestructMsgs() {
|
||||
// func (c *MsgTool) ConversationsDestructMsgs() {
|
||||
// log.ZInfo(context.Background(), "start msg destruct cron task")
|
||||
// ctx := mcontext.NewCtx(utils.GetSelfFuncName())
|
||||
// conversations, err := c.conversationDatabase.GetConversationIDsNeedDestruct(ctx)
|
||||
@@ -70,7 +71,7 @@ import (
|
||||
|
||||
func (c *MsgTool) ConversationsDestructMsgs() {
|
||||
log.ZInfo(context.Background(), "start msg destruct cron task")
|
||||
ctx := mcontext.NewCtx(utils.GetSelfFuncName())
|
||||
ctx := mcontext.NewCtx(stringutil.GetSelfFuncName())
|
||||
num, err := c.conversationDatabase.GetAllConversationIDsNumber(ctx)
|
||||
if err != nil {
|
||||
log.ZError(ctx, "GetAllConversationIDsNumber failed", err)
|
||||
@@ -117,7 +118,7 @@ func (c *MsgTool) ConversationsDestructMsgs() {
|
||||
}
|
||||
}
|
||||
for _, conversation := range temp {
|
||||
ctx = mcontext.NewCtx(utils.GetSelfFuncName() + "-" + utils.OperationIDGenerator() + "-" + conversation.ConversationID + "-" + conversation.OwnerUserID)
|
||||
ctx = mcontext.NewCtx(stringutil.GetSelfFuncName() + "-" + idutil.OperationIDGenerator() + "-" + conversation.ConversationID + "-" + conversation.OwnerUserID)
|
||||
log.ZDebug(
|
||||
ctx,
|
||||
"UserMsgsDestruct",
|
||||
@@ -141,9 +142,7 @@ func (c *MsgTool) ConversationsDestructMsgs() {
|
||||
log.ZError(ctx, "updateUsersConversationField failed", err, "conversationID", conversation.ConversationID, "ownerUserID", conversation.OwnerUserID)
|
||||
continue
|
||||
}
|
||||
if err := c.msgNotificationSender.UserDeleteMsgsNotification(ctx, conversation.OwnerUserID, conversation.ConversationID, seqs); err != nil {
|
||||
log.ZError(ctx, "userDeleteMsgsNotification failed", err, "conversationID", conversation.ConversationID, "ownerUserID", conversation.OwnerUserID)
|
||||
}
|
||||
c.msgNotificationSender.UserDeleteMsgsNotification(ctx, conversation.OwnerUserID, conversation.ConversationID, seqs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+29
-17
@@ -16,46 +16,58 @@ package tools
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/tools/db/redisutil"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"github.com/robfig/cron/v3"
|
||||
)
|
||||
|
||||
func StartTask(config *config.GlobalConfig) error {
|
||||
fmt.Println("cron task start, config", config.ChatRecordsClearTime)
|
||||
type CronTaskConfig struct {
|
||||
CronTask config.CronTask
|
||||
RedisConfig config.Redis
|
||||
MongodbConfig config.Mongo
|
||||
ZookeeperConfig config.ZooKeeper
|
||||
Share config.Share
|
||||
KafkaConfig config.Kafka
|
||||
}
|
||||
|
||||
msgTool, err := InitMsgTool(config)
|
||||
func Start(ctx context.Context, config *CronTaskConfig) error {
|
||||
|
||||
log.CInfo(ctx, "CRON-TASK server is initializing", "chatRecordsClearTime",
|
||||
config.CronTask.ChatRecordsClearTime, "msgDestructTime", config.CronTask.MsgDestructTime)
|
||||
|
||||
msgTool, err := InitMsgTool(ctx, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msgTool.convertTools()
|
||||
|
||||
rdb, err := cache.NewRedis(config)
|
||||
rdb, err := redisutil.NewRedisClient(ctx, config.RedisConfig.Build())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// register cron tasks
|
||||
var crontab = cron.New()
|
||||
fmt.Printf("Start chatRecordsClearTime cron task, cron config: %s\n", config.ChatRecordsClearTime)
|
||||
_, err = crontab.AddFunc(config.ChatRecordsClearTime, cronWrapFunc(config, rdb, "cron_clear_msg_and_fix_seq", msgTool.AllConversationClearMsgAndFixSeq))
|
||||
|
||||
_, err = crontab.AddFunc(config.CronTask.ChatRecordsClearTime,
|
||||
cronWrapFunc(config, rdb, "cron_clear_msg_and_fix_seq", msgTool.AllConversationClearMsgAndFixSeq))
|
||||
if err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
|
||||
fmt.Printf("Start msgDestruct cron task, cron config: %s\n", config.MsgDestructTime)
|
||||
_, err = crontab.AddFunc(config.MsgDestructTime, cronWrapFunc(config, rdb, "cron_conversations_destruct_msgs", msgTool.ConversationsDestructMsgs))
|
||||
_, err = crontab.AddFunc(config.CronTask.MsgDestructTime,
|
||||
cronWrapFunc(config, rdb, "cron_conversations_destruct_msgs", msgTool.ConversationsDestructMsgs))
|
||||
if err != nil {
|
||||
return errs.Wrap(err, "cron_conversations_destruct_msgs")
|
||||
return errs.WrapMsg(err, "cron_conversations_destruct_msgs")
|
||||
}
|
||||
|
||||
// start crontab
|
||||
@@ -66,10 +78,10 @@ func StartTask(config *config.GlobalConfig) error {
|
||||
<-sigs
|
||||
|
||||
// stop crontab, Wait for the running task to exit.
|
||||
ctx := crontab.Stop()
|
||||
cronCtx := crontab.Stop()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case <-cronCtx.Done():
|
||||
// graceful exit
|
||||
|
||||
case <-time.After(15 * time.Second):
|
||||
@@ -91,8 +103,8 @@ func netlock(rdb redis.UniversalClient, key string, ttl time.Duration) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
func cronWrapFunc(config *config.GlobalConfig, rdb redis.UniversalClient, key string, fn func()) func() {
|
||||
enableCronLocker := config.EnableCronLocker
|
||||
func cronWrapFunc(config *CronTaskConfig, rdb redis.UniversalClient, key string, fn func()) func() {
|
||||
enableCronLocker := config.CronTask.EnableCronLocker
|
||||
return func() {
|
||||
// if don't enable cron-locker, call fn directly.
|
||||
if !enableCronLocker {
|
||||
|
||||
@@ -15,22 +15,11 @@
|
||||
package tools
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
"github.com/robfig/cron/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
)
|
||||
|
||||
func TestDisLock(t *testing.T) {
|
||||
@@ -51,74 +40,74 @@ func TestDisLock(t *testing.T) {
|
||||
assert.Equal(t, true, netlock(rdb, "cron-2", 2*time.Second))
|
||||
}
|
||||
|
||||
func TestCronWrapFunc(t *testing.T) {
|
||||
rdb := redis.NewClient(&redis.Options{})
|
||||
defer rdb.Close()
|
||||
|
||||
once := sync.Once{}
|
||||
done := make(chan struct{}, 1)
|
||||
cb := func() {
|
||||
once.Do(func() {
|
||||
close(done)
|
||||
})
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
key := fmt.Sprintf("cron-%v", rand.Int31())
|
||||
crontab := cron.New(cron.WithSeconds())
|
||||
crontab.AddFunc("*/1 * * * * *", cronWrapFunc(config.NewGlobalConfig(), rdb, key, cb))
|
||||
crontab.Start()
|
||||
<-done
|
||||
|
||||
dur := time.Since(start)
|
||||
assert.LessOrEqual(t, dur.Seconds(), float64(2*time.Second))
|
||||
crontab.Stop()
|
||||
}
|
||||
|
||||
func TestCronWrapFuncWithNetlock(t *testing.T) {
|
||||
conf, err := initCfg()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
conf.EnableCronLocker = true
|
||||
rdb := redis.NewClient(&redis.Options{})
|
||||
defer rdb.Close()
|
||||
|
||||
done := make(chan string, 10)
|
||||
|
||||
crontab := cron.New(cron.WithSeconds())
|
||||
|
||||
key := fmt.Sprintf("cron-%v", rand.Int31())
|
||||
crontab.AddFunc("*/1 * * * * *", cronWrapFunc(conf, rdb, key, func() {
|
||||
done <- "host1"
|
||||
}))
|
||||
crontab.AddFunc("*/1 * * * * *", cronWrapFunc(conf, rdb, key, func() {
|
||||
done <- "host2"
|
||||
}))
|
||||
crontab.Start()
|
||||
|
||||
time.Sleep(12 * time.Second)
|
||||
// the ttl of netlock is 5s, so expected value is 2.
|
||||
assert.Equal(t, len(done), 2)
|
||||
|
||||
crontab.Stop()
|
||||
}
|
||||
|
||||
func initCfg() (*config.GlobalConfig, error) {
|
||||
const (
|
||||
defaultCfgPath = "../../../../../config/config.yaml"
|
||||
)
|
||||
|
||||
cfgPath := flag.String("c", defaultCfgPath, "Path to the configuration file")
|
||||
data, err := os.ReadFile(*cfgPath)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err, "ReadFile unmarshal failed")
|
||||
}
|
||||
|
||||
conf := config.NewGlobalConfig()
|
||||
err = yaml.Unmarshal(data, &conf)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err, "InitConfig unmarshal failed")
|
||||
}
|
||||
return conf, nil
|
||||
}
|
||||
//func TestCronWrapFunc(t *testing.T) {
|
||||
// rdb := redis.NewClient(&redis.Options{})
|
||||
// defer rdb.Close()
|
||||
//
|
||||
// once := sync.Once{}
|
||||
// done := make(chan struct{}, 1)
|
||||
// cb := func() {
|
||||
// once.Do(func() {
|
||||
// close(done)
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// start := time.Now()
|
||||
// key := fmt.Sprintf("cron-%v", rand.Int31())
|
||||
// crontab := cron.New(cron.WithSeconds())
|
||||
// crontab.AddFunc("*/1 * * * * *", cronWrapFunc(config.NewGlobalConfig(), rdb, key, cb))
|
||||
// crontab.Start()
|
||||
// <-done
|
||||
//
|
||||
// dur := time.Since(start)
|
||||
// assert.LessOrEqual(t, dur.Seconds(), float64(2*time.Second))
|
||||
// crontab.Stop()
|
||||
//}
|
||||
//
|
||||
//func TestCronWrapFuncWithNetlock(t *testing.T) {
|
||||
// conf, err := initCfg()
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
// conf.EnableCronLocker = true
|
||||
// rdb := redis.NewClient(&redis.Options{})
|
||||
// defer rdb.Close()
|
||||
//
|
||||
// done := make(chan string, 10)
|
||||
//
|
||||
// crontab := cron.New(cron.WithSeconds())
|
||||
//
|
||||
// key := fmt.Sprintf("cron-%v", rand.Int31())
|
||||
// crontab.AddFunc("*/1 * * * * *", cronWrapFunc(conf, rdb, key, func() {
|
||||
// done <- "host1"
|
||||
// }))
|
||||
// crontab.AddFunc("*/1 * * * * *", cronWrapFunc(conf, rdb, key, func() {
|
||||
// done <- "host2"
|
||||
// }))
|
||||
// crontab.Start()
|
||||
//
|
||||
// time.Sleep(12 * time.Second)
|
||||
// // the ttl of netlock is 5s, so expected value is 2.
|
||||
// assert.Equal(t, len(done), 2)
|
||||
//
|
||||
// crontab.Stop()
|
||||
//}
|
||||
//
|
||||
//func initCfg() (*config.GlobalConfig, error) {
|
||||
// const (
|
||||
// defaultCfgPath = "../../../../../config/config.yaml"
|
||||
// )
|
||||
//
|
||||
// cfgPath := flag.String("c", defaultCfgPath, "Path to the configuration file")
|
||||
// data, err := os.ReadFile(*cfgPath)
|
||||
// if err != nil {
|
||||
// return nil, errs.WrapMsg(err, "ReadFile unmarshal failed")
|
||||
// }
|
||||
//
|
||||
// conf := config.NewGlobalConfig()
|
||||
// err = yaml.Unmarshal(data, &conf)
|
||||
// if err != nil {
|
||||
// return nil, errs.WrapMsg(err, "InitConfig unmarshal failed")
|
||||
// }
|
||||
// return conf, nil
|
||||
//}
|
||||
|
||||
+75
-81
@@ -17,27 +17,18 @@ package tools
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/rpc/msg"
|
||||
"math"
|
||||
"math/rand"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/OpenIMSDK/tools/mcontext"
|
||||
"github.com/OpenIMSDK/tools/mw"
|
||||
"github.com/OpenIMSDK/tools/tx"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation"
|
||||
kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient/notification"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/util/conversationutil"
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/mcontext"
|
||||
"github.com/openimsdk/tools/utils/stringutil"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
)
|
||||
|
||||
type MsgTool struct {
|
||||
@@ -45,13 +36,13 @@ type MsgTool struct {
|
||||
conversationDatabase controller.ConversationDatabase
|
||||
userDatabase controller.UserDatabase
|
||||
groupDatabase controller.GroupDatabase
|
||||
msgNotificationSender *notification.MsgNotificationSender
|
||||
Config *config.GlobalConfig
|
||||
msgNotificationSender *msg.MsgNotificationSender
|
||||
config *CronTaskConfig
|
||||
}
|
||||
|
||||
func NewMsgTool(msgDatabase controller.CommonMsgDatabase, userDatabase controller.UserDatabase,
|
||||
groupDatabase controller.GroupDatabase, conversationDatabase controller.ConversationDatabase,
|
||||
msgNotificationSender *notification.MsgNotificationSender, config *config.GlobalConfig,
|
||||
msgNotificationSender *msg.MsgNotificationSender, config *CronTaskConfig,
|
||||
) *MsgTool {
|
||||
return &MsgTool{
|
||||
msgDatabase: msgDatabase,
|
||||
@@ -59,69 +50,71 @@ func NewMsgTool(msgDatabase controller.CommonMsgDatabase, userDatabase controlle
|
||||
groupDatabase: groupDatabase,
|
||||
conversationDatabase: conversationDatabase,
|
||||
msgNotificationSender: msgNotificationSender,
|
||||
Config: config,
|
||||
config: config,
|
||||
}
|
||||
}
|
||||
|
||||
func InitMsgTool(config *config.GlobalConfig) (*MsgTool, error) {
|
||||
rdb, err := cache.NewRedis(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mongo, err := unrelation.NewMongo(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
discov, err := kdisc.NewDiscoveryRegister(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
discov.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin")))
|
||||
userDB, err := mgo.NewUserMongo(mongo.GetDatabase(config.Mongo.Database))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
msgDatabase, err := controller.InitCommonMsgDatabase(rdb, mongo.GetDatabase(config.Mongo.Database), config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userMongoDB := unrelation.NewUserMongoDriver(mongo.GetDatabase(config.Mongo.Database))
|
||||
ctxTx := tx.NewMongo(mongo.GetClient())
|
||||
userDatabase := controller.NewUserDatabase(
|
||||
userDB,
|
||||
cache.NewUserCacheRedis(rdb, userDB, cache.GetDefaultOpt()),
|
||||
ctxTx,
|
||||
userMongoDB,
|
||||
)
|
||||
groupDB, err := mgo.NewGroupMongo(mongo.GetDatabase(config.Mongo.Database))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
groupMemberDB, err := mgo.NewGroupMember(mongo.GetDatabase(config.Mongo.Database))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
groupRequestDB, err := mgo.NewGroupRequestMgo(mongo.GetDatabase(config.Mongo.Database))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conversationDB, err := mgo.NewConversationMongo(mongo.GetDatabase(config.Mongo.Database))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
groupDatabase := controller.NewGroupDatabase(rdb, groupDB, groupMemberDB, groupRequestDB, ctxTx, nil)
|
||||
conversationDatabase := controller.NewConversationDatabase(
|
||||
conversationDB,
|
||||
cache.NewConversationRedis(rdb, cache.GetDefaultOpt(), conversationDB),
|
||||
ctxTx,
|
||||
)
|
||||
msgRpcClient := rpcclient.NewMessageRpcClient(discov, config)
|
||||
msgNotificationSender := notification.NewMsgNotificationSender(config, rpcclient.WithRpcClient(&msgRpcClient))
|
||||
msgTool := NewMsgTool(msgDatabase, userDatabase, groupDatabase, conversationDatabase, msgNotificationSender, config)
|
||||
return msgTool, nil
|
||||
func InitMsgTool(ctx context.Context, config *CronTaskConfig) (*MsgTool, error) {
|
||||
ch := make(chan int)
|
||||
<-ch
|
||||
//mgocli, err := mongoutil.NewMongoDB(ctx, config.MongodbConfig.Build())
|
||||
//if err != nil {
|
||||
// return nil, err
|
||||
//}
|
||||
//rdb, err := redisutil.NewRedisClient(ctx, config.RedisConfig.Build())
|
||||
//if err != nil {
|
||||
// return nil, err
|
||||
//}
|
||||
//discov, err := kdisc.NewDiscoveryRegister(&config.ZookeeperConfig, &config.Share)
|
||||
//if err != nil {
|
||||
// return nil, err
|
||||
//}
|
||||
//discov.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin")))
|
||||
//userDB, err := mgo.NewUserMongo(mgocli.GetDB())
|
||||
//if err != nil {
|
||||
// return nil, err
|
||||
//}
|
||||
////msgDatabase, err := controller.InitCommonMsgDatabase(rdb, mgocli.GetDB(), config)
|
||||
//if err != nil {
|
||||
// return nil, err
|
||||
//}
|
||||
//userMongoDB := mgo.NewUserMongoDriver(mgocli.GetDB())
|
||||
//userDatabase := controller.NewUserDatabase(
|
||||
// userDB,
|
||||
// cache.NewUserCacheRedis(rdb, userDB, cache.GetDefaultOpt()),
|
||||
// mgocli.GetTx(),
|
||||
// userMongoDB,
|
||||
//)
|
||||
//groupDB, err := mgo.NewGroupMongo(mgocli.GetDB())
|
||||
//if err != nil {
|
||||
// return nil, err
|
||||
//}
|
||||
//groupMemberDB, err := mgo.NewGroupMember(mgocli.GetDB())
|
||||
//if err != nil {
|
||||
// return nil, err
|
||||
//}
|
||||
//groupRequestDB, err := mgo.NewGroupRequestMgo(mgocli.GetDB())
|
||||
//if err != nil {
|
||||
// return nil, err
|
||||
//}
|
||||
//conversationDB, err := mgo.NewConversationMongo(mgocli.GetDB())
|
||||
//if err != nil {
|
||||
// return nil, err
|
||||
//}
|
||||
//groupDatabase := controller.NewGroupDatabase(rdb, groupDB, groupMemberDB, groupRequestDB, mgocli.GetTx(), nil)
|
||||
//conversationDatabase := controller.NewConversationDatabase(
|
||||
// conversationDB,
|
||||
// cache.NewConversationRedis(rdb, cache.GetDefaultOpt(), conversationDB),
|
||||
// mgocli.GetTx(),
|
||||
//)
|
||||
//msgRpcClient := rpcclient.NewMessageRpcClient(discov, config.Share.RpcRegisterName.Msg)
|
||||
//msgNotificationSender := notification.NewMsgNotificationSender(config, rpcclient.WithRpcClient(&msgRpcClient))
|
||||
//msgTool := NewMsgTool(msgDatabase, userDatabase, groupDatabase, conversationDatabase, msgNotificationSender, config)
|
||||
//return msgTool, nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
//func (c *MsgTool) AllConversationClearMsgAndFixSeq() {
|
||||
// func (c *MsgTool) AllConversationClearMsgAndFixSeq() {
|
||||
// ctx := mcontext.NewCtx(utils.GetSelfFuncName())
|
||||
// log.ZInfo(ctx, "============================ start del cron task ============================")
|
||||
// conversationIDs, err := c.conversationDatabase.GetAllConversationIDs(ctx)
|
||||
@@ -137,7 +130,7 @@ func InitMsgTool(config *config.GlobalConfig) (*MsgTool, error) {
|
||||
//}
|
||||
|
||||
func (c *MsgTool) AllConversationClearMsgAndFixSeq() {
|
||||
ctx := mcontext.NewCtx(utils.GetSelfFuncName())
|
||||
ctx := mcontext.NewCtx(stringutil.GetSelfFuncName())
|
||||
log.ZInfo(ctx, "============================ start del cron task ============================")
|
||||
num, err := c.conversationDatabase.GetAllConversationIDsNumber(ctx)
|
||||
if err != nil {
|
||||
@@ -179,8 +172,9 @@ func (c *MsgTool) AllConversationClearMsgAndFixSeq() {
|
||||
|
||||
func (c *MsgTool) ClearConversationsMsg(ctx context.Context, conversationIDs []string) {
|
||||
for _, conversationID := range conversationIDs {
|
||||
if err := c.msgDatabase.DeleteConversationMsgsAndSetMinSeq(ctx, conversationID, int64(c.Config.RetainChatRecords*24*60*60)); err != nil {
|
||||
log.ZError(ctx, "DeleteUserSuperGroupMsgsAndSetMinSeq failed", err, "conversationID", conversationID, "DBRetainChatRecords", c.Config.RetainChatRecords)
|
||||
if err := c.msgDatabase.DeleteConversationMsgsAndSetMinSeq(ctx, conversationID, int64(c.config.CronTask.RetainChatRecords*24*60*60)); err != nil {
|
||||
log.ZError(ctx, "DeleteUserSuperGroupMsgsAndSetMinSeq failed", err, "conversationID",
|
||||
conversationID, "DBRetainChatRecords", c.config.CronTask.RetainChatRecords)
|
||||
}
|
||||
if err := c.checkMaxSeq(ctx, conversationID); err != nil {
|
||||
log.ZError(ctx, "fixSeq failed", err, "conversationID", conversationID)
|
||||
@@ -220,7 +214,7 @@ func (c *MsgTool) FixAllSeq(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
for _, conversationID := range conversationIDs {
|
||||
conversationIDs = append(conversationIDs, utils.GetNotificationConversationIDByConversationID(conversationID))
|
||||
conversationIDs = append(conversationIDs, conversationutil.GetNotificationConversationIDByConversationID(conversationID))
|
||||
}
|
||||
for _, conversationID := range conversationIDs {
|
||||
if err := c.checkMaxSeq(ctx, conversationID); err != nil {
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
package tools
|
||||
|
||||
import (
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/OpenIMSDK/tools/mcontext"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/mcontext"
|
||||
)
|
||||
|
||||
func (c *MsgTool) convertTools() {
|
||||
|
||||
Reference in New Issue
Block a user