commit
23a1626ac1
@ -1,7 +1,14 @@
|
|||||||
address: [localhost:16379]
|
address: [localhost:16379]
|
||||||
username:
|
username:
|
||||||
password: openIM123
|
password: openIM123
|
||||||
clusterMode: false
|
# redis Mode, including "standalone","cluster","sentinel"
|
||||||
|
redisMode: "standalone"
|
||||||
db: 0
|
db: 0
|
||||||
maxRetry: 10
|
maxRetry: 10
|
||||||
poolSize: 100
|
poolSize: 100
|
||||||
|
# Sentinel configuration (only used when redisMode is "sentinel")
|
||||||
|
sentinelMode:
|
||||||
|
masterName: "redis-master"
|
||||||
|
sentinelsAddrs: ["127.0.0.1:26379", "127.0.0.1:26380", "127.0.0.1:26381"]
|
||||||
|
routeByLatency: true
|
||||||
|
routeRandomly: true
|
||||||
|
@ -1,9 +1,20 @@
|
|||||||
secret: openIM123
|
secret: openIM123
|
||||||
|
|
||||||
imAdminUserID: [ imAdmin ]
|
# imAdminUser: Configuration for instant messaging system administrators
|
||||||
|
imAdminUser:
|
||||||
|
# userIDs: List of administrator user IDs.
|
||||||
|
# Each entry here corresponds by index to the matching entry in the nicknames list below.
|
||||||
|
userIDs: [imAdmin]
|
||||||
|
# nicknames: List of administrator display names.
|
||||||
|
# Each entry here corresponds by index to the matching entry in the userIDs list above.
|
||||||
|
nicknames: [superAdmin]
|
||||||
|
|
||||||
# 1: For Android, iOS, Windows, Mac, and web platforms, only one instance can be online at a time
|
# 1: For Android, iOS, Windows, Mac, and web platforms, only one instance can be online at a time
|
||||||
multiLogin:
|
multiLogin:
|
||||||
policy: 1
|
policy: 1
|
||||||
# max num of tokens in one end
|
# max num of tokens in one end
|
||||||
maxNumOneEnd: 30
|
maxNumOneEnd: 30
|
||||||
|
|
||||||
|
rpcMaxBodySize:
|
||||||
|
requestMaxBodySize: 8388608
|
||||||
|
responseMaxBodySize: 8388608
|
||||||
|
@ -0,0 +1,132 @@
|
|||||||
|
package msgtransfer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
|
||||||
|
"github.com/openimsdk/open-im-server/v3/pkg/apistruct"
|
||||||
|
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||||
|
"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/datautil"
|
||||||
|
"github.com/openimsdk/tools/utils/stringutil"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
|
cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
|
||||||
|
)
|
||||||
|
|
||||||
|
func toCommonCallback(ctx context.Context, msg *sdkws.MsgData, command string) cbapi.CommonCallbackReq {
|
||||||
|
return cbapi.CommonCallbackReq{
|
||||||
|
SendID: msg.SendID,
|
||||||
|
ServerMsgID: msg.ServerMsgID,
|
||||||
|
CallbackCommand: command,
|
||||||
|
ClientMsgID: msg.ClientMsgID,
|
||||||
|
OperationID: mcontext.GetOperationID(ctx),
|
||||||
|
SenderPlatformID: msg.SenderPlatformID,
|
||||||
|
SenderNickname: msg.SenderNickname,
|
||||||
|
SessionType: msg.SessionType,
|
||||||
|
MsgFrom: msg.MsgFrom,
|
||||||
|
ContentType: msg.ContentType,
|
||||||
|
Status: msg.Status,
|
||||||
|
SendTime: msg.SendTime,
|
||||||
|
CreateTime: msg.CreateTime,
|
||||||
|
AtUserIDList: msg.AtUserIDList,
|
||||||
|
SenderFaceURL: msg.SenderFaceURL,
|
||||||
|
Content: GetContent(msg),
|
||||||
|
Seq: uint32(msg.Seq),
|
||||||
|
Ex: msg.Ex,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
} else {
|
||||||
|
return string(msg.Content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mc *OnlineHistoryMongoConsumerHandler) webhookAfterSendSingleMsg(ctx context.Context, after *config.AfterConfig, msg *sdkws.MsgData) {
|
||||||
|
if msg.ContentType == constant.Typing {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !filterAfterMsg(msg, after) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cbReq := &cbapi.CallbackAfterSendSingleMsgReq{
|
||||||
|
CommonCallbackReq: toCommonCallback(ctx, msg, cbapi.CallbackAfterSendSingleMsgCommand),
|
||||||
|
RecvID: msg.RecvID,
|
||||||
|
}
|
||||||
|
mc.webhookClient.AsyncPostWithQuery(ctx, cbReq.GetCallbackCommand(), cbReq, &cbapi.CallbackAfterSendSingleMsgResp{}, after, buildKeyMsgDataQuery(msg))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mc *OnlineHistoryMongoConsumerHandler) webhookAfterSendGroupMsg(ctx context.Context, after *config.AfterConfig, msg *sdkws.MsgData) {
|
||||||
|
if msg.ContentType == constant.Typing {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !filterAfterMsg(msg, after) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cbReq := &cbapi.CallbackAfterSendGroupMsgReq{
|
||||||
|
CommonCallbackReq: toCommonCallback(ctx, msg, cbapi.CallbackAfterSendGroupMsgCommand),
|
||||||
|
GroupID: msg.GroupID,
|
||||||
|
}
|
||||||
|
|
||||||
|
mc.webhookClient.AsyncPostWithQuery(ctx, cbReq.GetCallbackCommand(), cbReq, &cbapi.CallbackAfterSendGroupMsgResp{}, after, buildKeyMsgDataQuery(msg))
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildKeyMsgDataQuery(msg *sdkws.MsgData) map[string]string {
|
||||||
|
keyMsgData := apistruct.KeyMsgData{
|
||||||
|
SendID: msg.SendID,
|
||||||
|
RecvID: msg.RecvID,
|
||||||
|
GroupID: msg.GroupID,
|
||||||
|
}
|
||||||
|
|
||||||
|
return map[string]string{
|
||||||
|
webhook.Key: base64.StdEncoding.EncodeToString(stringutil.StructToJsonBytes(keyMsgData)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterAfterMsg(msg *sdkws.MsgData, after *config.AfterConfig) bool {
|
||||||
|
return filterMsg(msg, after.AttentionIds, after.DeniedTypes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterMsg(msg *sdkws.MsgData, attentionIds []string, deniedTypes []int32) bool {
|
||||||
|
// According to the attentionIds configuration, only some users are sent
|
||||||
|
if len(attentionIds) != 0 && msg.ContentType == constant.SingleChatType && !datautil.Contain(msg.RecvID, attentionIds...) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(attentionIds) != 0 && msg.ContentType == constant.ReadGroupChatType && !datautil.Contain(msg.GroupID, attentionIds...) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if defaultDeniedTypes(msg.ContentType) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(deniedTypes) != 0 && datautil.Contain(msg.ContentType, deniedTypes...) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultDeniedTypes(contentType int32) bool {
|
||||||
|
if contentType >= constant.NotificationBegin && contentType <= constant.NotificationEnd {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if contentType == constant.Typing {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
@ -0,0 +1,117 @@
|
|||||||
|
package conversation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
|
||||||
|
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||||
|
dbModel "github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
|
||||||
|
"github.com/openimsdk/open-im-server/v3/pkg/common/webhook"
|
||||||
|
"github.com/openimsdk/tools/utils/datautil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *conversationServer) webhookBeforeCreateSingleChatConversations(ctx context.Context, before *config.BeforeConfig, req *dbModel.Conversation) error {
|
||||||
|
return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
|
||||||
|
cbReq := &callbackstruct.CallbackBeforeCreateSingleChatConversationsReq{
|
||||||
|
CallbackCommand: callbackstruct.CallbackBeforeCreateSingleChatConversationsCommand,
|
||||||
|
OwnerUserID: req.OwnerUserID,
|
||||||
|
ConversationID: req.ConversationID,
|
||||||
|
ConversationType: req.ConversationType,
|
||||||
|
UserID: req.UserID,
|
||||||
|
RecvMsgOpt: req.RecvMsgOpt,
|
||||||
|
IsPinned: req.IsPinned,
|
||||||
|
IsPrivateChat: req.IsPrivateChat,
|
||||||
|
BurnDuration: req.BurnDuration,
|
||||||
|
GroupAtType: req.GroupAtType,
|
||||||
|
AttachedInfo: req.AttachedInfo,
|
||||||
|
Ex: req.Ex,
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := &callbackstruct.CallbackBeforeCreateSingleChatConversationsResp{}
|
||||||
|
|
||||||
|
if err := c.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
datautil.NotNilReplace(&req.RecvMsgOpt, resp.RecvMsgOpt)
|
||||||
|
datautil.NotNilReplace(&req.IsPinned, resp.IsPinned)
|
||||||
|
datautil.NotNilReplace(&req.IsPrivateChat, resp.IsPrivateChat)
|
||||||
|
datautil.NotNilReplace(&req.BurnDuration, resp.BurnDuration)
|
||||||
|
datautil.NotNilReplace(&req.GroupAtType, resp.GroupAtType)
|
||||||
|
datautil.NotNilReplace(&req.AttachedInfo, resp.AttachedInfo)
|
||||||
|
datautil.NotNilReplace(&req.Ex, resp.Ex)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conversationServer) webhookAfterCreateSingleChatConversations(ctx context.Context, after *config.AfterConfig, req *dbModel.Conversation) error {
|
||||||
|
cbReq := &callbackstruct.CallbackAfterCreateSingleChatConversationsReq{
|
||||||
|
CallbackCommand: callbackstruct.CallbackAfterCreateSingleChatConversationsCommand,
|
||||||
|
OwnerUserID: req.OwnerUserID,
|
||||||
|
ConversationID: req.ConversationID,
|
||||||
|
ConversationType: req.ConversationType,
|
||||||
|
UserID: req.UserID,
|
||||||
|
RecvMsgOpt: req.RecvMsgOpt,
|
||||||
|
IsPinned: req.IsPinned,
|
||||||
|
IsPrivateChat: req.IsPrivateChat,
|
||||||
|
BurnDuration: req.BurnDuration,
|
||||||
|
GroupAtType: req.GroupAtType,
|
||||||
|
AttachedInfo: req.AttachedInfo,
|
||||||
|
Ex: req.Ex,
|
||||||
|
}
|
||||||
|
|
||||||
|
c.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &callbackstruct.CallbackAfterCreateSingleChatConversationsResp{}, after)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conversationServer) webhookBeforeCreateGroupChatConversations(ctx context.Context, before *config.BeforeConfig, req *dbModel.Conversation) error {
|
||||||
|
return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
|
||||||
|
cbReq := &callbackstruct.CallbackBeforeCreateGroupChatConversationsReq{
|
||||||
|
CallbackCommand: callbackstruct.CallbackBeforeCreateGroupChatConversationsCommand,
|
||||||
|
ConversationID: req.ConversationID,
|
||||||
|
ConversationType: req.ConversationType,
|
||||||
|
GroupID: req.GroupID,
|
||||||
|
RecvMsgOpt: req.RecvMsgOpt,
|
||||||
|
IsPinned: req.IsPinned,
|
||||||
|
IsPrivateChat: req.IsPrivateChat,
|
||||||
|
BurnDuration: req.BurnDuration,
|
||||||
|
GroupAtType: req.GroupAtType,
|
||||||
|
AttachedInfo: req.AttachedInfo,
|
||||||
|
Ex: req.Ex,
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := &callbackstruct.CallbackBeforeCreateGroupChatConversationsResp{}
|
||||||
|
|
||||||
|
if err := c.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
datautil.NotNilReplace(&req.RecvMsgOpt, resp.RecvMsgOpt)
|
||||||
|
datautil.NotNilReplace(&req.IsPinned, resp.IsPinned)
|
||||||
|
datautil.NotNilReplace(&req.IsPrivateChat, resp.IsPrivateChat)
|
||||||
|
datautil.NotNilReplace(&req.BurnDuration, resp.BurnDuration)
|
||||||
|
datautil.NotNilReplace(&req.GroupAtType, resp.GroupAtType)
|
||||||
|
datautil.NotNilReplace(&req.AttachedInfo, resp.AttachedInfo)
|
||||||
|
datautil.NotNilReplace(&req.Ex, resp.Ex)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conversationServer) webhookAfterCreateGroupChatConversations(ctx context.Context, after *config.AfterConfig, req *dbModel.Conversation) error {
|
||||||
|
cbReq := &callbackstruct.CallbackAfterCreateGroupChatConversationsReq{
|
||||||
|
CallbackCommand: callbackstruct.CallbackAfterCreateGroupChatConversationsCommand,
|
||||||
|
ConversationID: req.ConversationID,
|
||||||
|
ConversationType: req.ConversationType,
|
||||||
|
GroupID: req.GroupID,
|
||||||
|
RecvMsgOpt: req.RecvMsgOpt,
|
||||||
|
IsPinned: req.IsPinned,
|
||||||
|
IsPrivateChat: req.IsPrivateChat,
|
||||||
|
BurnDuration: req.BurnDuration,
|
||||||
|
GroupAtType: req.GroupAtType,
|
||||||
|
AttachedInfo: req.AttachedInfo,
|
||||||
|
Ex: req.Ex,
|
||||||
|
}
|
||||||
|
|
||||||
|
c.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &callbackstruct.CallbackAfterCreateGroupChatConversationsResp{}, after)
|
||||||
|
return nil
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
package conversation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
dbModel "github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
|
||||||
|
"github.com/openimsdk/protocol/conversation"
|
||||||
|
)
|
||||||
|
|
||||||
|
func UpdateConversationsMap(ctx context.Context, req *conversation.SetConversationsReq) (m map[string]any, conversation dbModel.Conversation, err error) {
|
||||||
|
m = make(map[string]any)
|
||||||
|
|
||||||
|
conversation.ConversationID = req.Conversation.ConversationID
|
||||||
|
conversation.ConversationType = req.Conversation.ConversationType
|
||||||
|
conversation.UserID = req.Conversation.UserID
|
||||||
|
conversation.GroupID = req.Conversation.GroupID
|
||||||
|
|
||||||
|
if req.Conversation.RecvMsgOpt != nil {
|
||||||
|
conversation.RecvMsgOpt = req.Conversation.RecvMsgOpt.Value
|
||||||
|
m["recv_msg_opt"] = req.Conversation.RecvMsgOpt.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Conversation.AttachedInfo != nil {
|
||||||
|
conversation.AttachedInfo = req.Conversation.AttachedInfo.Value
|
||||||
|
m["attached_info"] = req.Conversation.AttachedInfo.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Conversation.Ex != nil {
|
||||||
|
conversation.Ex = req.Conversation.Ex.Value
|
||||||
|
m["ex"] = req.Conversation.Ex.Value
|
||||||
|
}
|
||||||
|
if req.Conversation.IsPinned != nil {
|
||||||
|
conversation.IsPinned = req.Conversation.IsPinned.Value
|
||||||
|
m["is_pinned"] = req.Conversation.IsPinned.Value
|
||||||
|
}
|
||||||
|
if req.Conversation.GroupAtType != nil {
|
||||||
|
conversation.GroupAtType = req.Conversation.GroupAtType.Value
|
||||||
|
m["group_at_type"] = req.Conversation.GroupAtType.Value
|
||||||
|
}
|
||||||
|
if req.Conversation.MsgDestructTime != nil {
|
||||||
|
conversation.MsgDestructTime = req.Conversation.MsgDestructTime.Value
|
||||||
|
m["msg_destruct_time"] = req.Conversation.MsgDestructTime.Value
|
||||||
|
}
|
||||||
|
if req.Conversation.IsMsgDestruct != nil {
|
||||||
|
conversation.IsMsgDestruct = req.Conversation.IsMsgDestruct.Value
|
||||||
|
m["is_msg_destruct"] = req.Conversation.IsMsgDestruct.Value
|
||||||
|
}
|
||||||
|
if req.Conversation.BurnDuration != nil {
|
||||||
|
conversation.BurnDuration = req.Conversation.BurnDuration.Value
|
||||||
|
m["burn_duration"] = req.Conversation.BurnDuration.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
return m, conversation, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func UserUpdateCheckMap(ctx context.Context, userID string, req *conversation.ConversationReq, conversation *dbModel.Conversation) (unequal bool) {
|
||||||
|
unequal = false
|
||||||
|
|
||||||
|
if req.RecvMsgOpt != nil && conversation.RecvMsgOpt != req.RecvMsgOpt.Value {
|
||||||
|
unequal = true
|
||||||
|
}
|
||||||
|
if req.AttachedInfo != nil && conversation.AttachedInfo != req.AttachedInfo.Value {
|
||||||
|
unequal = true
|
||||||
|
}
|
||||||
|
if req.Ex != nil && conversation.Ex != req.Ex.Value {
|
||||||
|
unequal = true
|
||||||
|
}
|
||||||
|
if req.IsPinned != nil && conversation.IsPinned != req.IsPinned.Value {
|
||||||
|
unequal = true
|
||||||
|
}
|
||||||
|
if req.GroupAtType != nil && conversation.GroupAtType != req.GroupAtType.Value {
|
||||||
|
unequal = true
|
||||||
|
}
|
||||||
|
if req.MsgDestructTime != nil && conversation.MsgDestructTime != req.MsgDestructTime.Value {
|
||||||
|
unequal = true
|
||||||
|
}
|
||||||
|
if req.IsMsgDestruct != nil && conversation.IsMsgDestruct != req.IsMsgDestruct.Value {
|
||||||
|
unequal = true
|
||||||
|
}
|
||||||
|
if req.BurnDuration != nil && conversation.BurnDuration != req.BurnDuration.Value {
|
||||||
|
unequal = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return unequal
|
||||||
|
}
|
@ -1,115 +0,0 @@
|
|||||||
package msg
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
|
|
||||||
"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/errs"
|
|
||||||
)
|
|
||||||
|
|
||||||
const StreamDeadlineTime = time.Second * 60 * 10
|
|
||||||
|
|
||||||
func (m *msgServer) handlerStreamMsg(ctx context.Context, msgData *sdkws.MsgData) error {
|
|
||||||
now := time.Now()
|
|
||||||
val := &model.StreamMsg{
|
|
||||||
ClientMsgID: msgData.ClientMsgID,
|
|
||||||
ConversationID: msgprocessor.GetConversationIDByMsg(msgData),
|
|
||||||
UserID: msgData.SendID,
|
|
||||||
CreateTime: now,
|
|
||||||
DeadlineTime: now.Add(StreamDeadlineTime),
|
|
||||||
}
|
|
||||||
return m.StreamMsgDatabase.CreateStreamMsg(ctx, val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *msgServer) getStreamMsg(ctx context.Context, clientMsgID string) (*model.StreamMsg, error) {
|
|
||||||
res, err := m.StreamMsgDatabase.GetStreamMsg(ctx, clientMsgID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
now := time.Now()
|
|
||||||
if !res.End && res.DeadlineTime.Before(now) {
|
|
||||||
res.End = true
|
|
||||||
res.DeadlineTime = now
|
|
||||||
_ = m.StreamMsgDatabase.AppendStreamMsg(ctx, res.ClientMsgID, 0, nil, true, now)
|
|
||||||
}
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *msgServer) AppendStreamMsg(ctx context.Context, req *msg.AppendStreamMsgReq) (*msg.AppendStreamMsgResp, error) {
|
|
||||||
res, err := m.getStreamMsg(ctx, req.ClientMsgID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if res.End {
|
|
||||||
return nil, errs.ErrNoPermission.WrapMsg("stream msg is end")
|
|
||||||
}
|
|
||||||
if len(res.Packets) < int(req.StartIndex) {
|
|
||||||
return nil, errs.ErrNoPermission.WrapMsg("start index is invalid")
|
|
||||||
}
|
|
||||||
if val := len(res.Packets) - int(req.StartIndex); val > 0 {
|
|
||||||
exist := res.Packets[int(req.StartIndex):]
|
|
||||||
for i, s := range exist {
|
|
||||||
if len(req.Packets) == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if s != req.Packets[i] {
|
|
||||||
return nil, errs.ErrNoPermission.WrapMsg(fmt.Sprintf("packet %d has been written and is inconsistent", i))
|
|
||||||
}
|
|
||||||
req.StartIndex++
|
|
||||||
req.Packets = req.Packets[1:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(req.Packets) == 0 && res.End == req.End {
|
|
||||||
return &msg.AppendStreamMsgResp{}, nil
|
|
||||||
}
|
|
||||||
deadlineTime := time.Now().Add(StreamDeadlineTime)
|
|
||||||
if err := m.StreamMsgDatabase.AppendStreamMsg(ctx, req.ClientMsgID, int(req.StartIndex), req.Packets, req.End, deadlineTime); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
conversation, err := m.conversationClient.GetConversation(ctx, res.ConversationID, res.UserID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
tips := &sdkws.StreamMsgTips{
|
|
||||||
ConversationID: res.ConversationID,
|
|
||||||
ClientMsgID: res.ClientMsgID,
|
|
||||||
StartIndex: req.StartIndex,
|
|
||||||
Packets: req.Packets,
|
|
||||||
End: req.End,
|
|
||||||
}
|
|
||||||
var (
|
|
||||||
recvID string
|
|
||||||
sessionType int32
|
|
||||||
)
|
|
||||||
if conversation.GroupID == "" {
|
|
||||||
sessionType = constant.SingleChatType
|
|
||||||
recvID = conversation.UserID
|
|
||||||
} else {
|
|
||||||
sessionType = constant.ReadGroupChatType
|
|
||||||
recvID = conversation.GroupID
|
|
||||||
}
|
|
||||||
m.msgNotificationSender.StreamMsgNotification(ctx, res.UserID, recvID, sessionType, tips)
|
|
||||||
return &msg.AppendStreamMsgResp{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *msgServer) GetStreamMsg(ctx context.Context, req *msg.GetStreamMsgReq) (*msg.GetStreamMsgResp, error) {
|
|
||||||
res, err := m.getStreamMsg(ctx, req.ClientMsgID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &msg.GetStreamMsgResp{
|
|
||||||
ClientMsgID: res.ClientMsgID,
|
|
||||||
ConversationID: res.ConversationID,
|
|
||||||
UserID: res.UserID,
|
|
||||||
Packets: res.Packets,
|
|
||||||
End: res.End,
|
|
||||||
CreateTime: res.CreateTime.UnixMilli(),
|
|
||||||
DeadlineTime: res.DeadlineTime.UnixMilli(),
|
|
||||||
}, nil
|
|
||||||
}
|
|
@ -0,0 +1,71 @@
|
|||||||
|
package user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
|
||||||
|
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
|
||||||
|
pbuser "github.com/openimsdk/protocol/user"
|
||||||
|
"github.com/openimsdk/tools/utils/datautil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *userServer) GetUserClientConfig(ctx context.Context, req *pbuser.GetUserClientConfigReq) (*pbuser.GetUserClientConfigResp, error) {
|
||||||
|
if req.UserID != "" {
|
||||||
|
if err := authverify.CheckAccess(ctx, req.UserID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if _, err := s.db.GetUserByID(ctx, req.UserID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res, err := s.clientConfig.GetUserConfig(ctx, req.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &pbuser.GetUserClientConfigResp{Configs: res}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *userServer) SetUserClientConfig(ctx context.Context, req *pbuser.SetUserClientConfigReq) (*pbuser.SetUserClientConfigResp, error) {
|
||||||
|
if err := authverify.CheckAdmin(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if req.UserID != "" {
|
||||||
|
if _, err := s.db.GetUserByID(ctx, req.UserID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := s.clientConfig.SetUserConfig(ctx, req.UserID, req.Configs); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &pbuser.SetUserClientConfigResp{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *userServer) DelUserClientConfig(ctx context.Context, req *pbuser.DelUserClientConfigReq) (*pbuser.DelUserClientConfigResp, error) {
|
||||||
|
if err := authverify.CheckAdmin(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := s.clientConfig.DelUserConfig(ctx, req.UserID, req.Keys); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &pbuser.DelUserClientConfigResp{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *userServer) PageUserClientConfig(ctx context.Context, req *pbuser.PageUserClientConfigReq) (*pbuser.PageUserClientConfigResp, error) {
|
||||||
|
if err := authverify.CheckAdmin(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
total, res, err := s.clientConfig.GetUserConfigPage(ctx, req.UserID, req.Key, req.Pagination)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &pbuser.PageUserClientConfigResp{
|
||||||
|
Total: total,
|
||||||
|
Configs: datautil.Slice(res, func(e *model.ClientConfig) *pbuser.ClientConfig {
|
||||||
|
return &pbuser.ClientConfig{
|
||||||
|
UserID: e.UserID,
|
||||||
|
Key: e.Key,
|
||||||
|
Value: e.Value,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}, nil
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
package cron
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/openimsdk/tools/log"
|
||||||
|
clientv3 "go.etcd.io/etcd/client/v3"
|
||||||
|
"go.etcd.io/etcd/client/v3/concurrency"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
lockLeaseTTL = 300
|
||||||
|
)
|
||||||
|
|
||||||
|
type EtcdLocker struct {
|
||||||
|
client *clientv3.Client
|
||||||
|
instanceID string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEtcdLocker creates a new etcd distributed lock
|
||||||
|
func NewEtcdLocker(client *clientv3.Client) (*EtcdLocker, error) {
|
||||||
|
hostname, _ := os.Hostname()
|
||||||
|
pid := os.Getpid()
|
||||||
|
instanceID := fmt.Sprintf("%s-pid-%d-%d", hostname, pid, time.Now().UnixNano())
|
||||||
|
|
||||||
|
locker := &EtcdLocker{
|
||||||
|
client: client,
|
||||||
|
instanceID: instanceID,
|
||||||
|
}
|
||||||
|
|
||||||
|
return locker, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EtcdLocker) ExecuteWithLock(ctx context.Context, taskName string, task func()) {
|
||||||
|
session, err := concurrency.NewSession(e.client, concurrency.WithTTL(lockLeaseTTL))
|
||||||
|
if err != nil {
|
||||||
|
log.ZWarn(ctx, "Failed to create etcd session", err,
|
||||||
|
"taskName", taskName,
|
||||||
|
"instanceID", e.instanceID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer session.Close()
|
||||||
|
|
||||||
|
lockKey := fmt.Sprintf("openim/crontask/%s", taskName)
|
||||||
|
mutex := concurrency.NewMutex(session, lockKey)
|
||||||
|
|
||||||
|
ctxWithTimeout, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
err = mutex.TryLock(ctxWithTimeout)
|
||||||
|
if err != nil {
|
||||||
|
// errors.Is(err, concurrency.ErrLocked)
|
||||||
|
log.ZDebug(ctx, "Task is being executed by another instance, skipping",
|
||||||
|
"taskName", taskName,
|
||||||
|
"instanceID", e.instanceID,
|
||||||
|
"error", err.Error())
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if err := mutex.Unlock(ctx); err != nil {
|
||||||
|
log.ZWarn(ctx, "Failed to release task lock", err,
|
||||||
|
"taskName", taskName,
|
||||||
|
"instanceID", e.instanceID)
|
||||||
|
} else {
|
||||||
|
log.ZInfo(ctx, "Successfully released task lock",
|
||||||
|
"taskName", taskName,
|
||||||
|
"instanceID", e.instanceID)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
log.ZInfo(ctx, "Successfully acquired task lock, starting execution",
|
||||||
|
"taskName", taskName,
|
||||||
|
"instanceID", e.instanceID,
|
||||||
|
"sessionID", session.Lease())
|
||||||
|
|
||||||
|
task()
|
||||||
|
|
||||||
|
log.ZInfo(ctx, "Task execution completed",
|
||||||
|
"taskName", taskName,
|
||||||
|
"instanceID", e.instanceID)
|
||||||
|
}
|
@ -1 +0,0 @@
|
|||||||
package apistruct
|
|
@ -0,0 +1,91 @@
|
|||||||
|
package callbackstruct
|
||||||
|
|
||||||
|
type CallbackBeforeCreateSingleChatConversationsReq struct {
|
||||||
|
CallbackCommand `json:"callbackCommand"`
|
||||||
|
OwnerUserID string `json:"owner_user_id"`
|
||||||
|
ConversationID string `json:"conversation_id"`
|
||||||
|
ConversationType int32 `json:"conversation_type"`
|
||||||
|
UserID string `json:"user_id"`
|
||||||
|
RecvMsgOpt int32 `json:"recv_msg_opt"`
|
||||||
|
IsPinned bool `json:"is_pinned"`
|
||||||
|
IsPrivateChat bool `json:"is_private_chat"`
|
||||||
|
BurnDuration int32 `json:"burn_duration"`
|
||||||
|
GroupAtType int32 `json:"group_at_type"`
|
||||||
|
AttachedInfo string `json:"attached_info"`
|
||||||
|
Ex string `json:"ex"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CallbackBeforeCreateSingleChatConversationsResp struct {
|
||||||
|
CommonCallbackResp
|
||||||
|
RecvMsgOpt *int32 `json:"recv_msg_opt"`
|
||||||
|
IsPinned *bool `json:"is_pinned"`
|
||||||
|
IsPrivateChat *bool `json:"is_private_chat"`
|
||||||
|
BurnDuration *int32 `json:"burn_duration"`
|
||||||
|
GroupAtType *int32 `json:"group_at_type"`
|
||||||
|
AttachedInfo *string `json:"attached_info"`
|
||||||
|
Ex *string `json:"ex"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CallbackAfterCreateSingleChatConversationsReq struct {
|
||||||
|
CallbackCommand `json:"callbackCommand"`
|
||||||
|
OwnerUserID string `json:"owner_user_id"`
|
||||||
|
ConversationID string `json:"conversation_id"`
|
||||||
|
ConversationType int32 `json:"conversation_type"`
|
||||||
|
UserID string `json:"user_id"`
|
||||||
|
RecvMsgOpt int32 `json:"recv_msg_opt"`
|
||||||
|
IsPinned bool `json:"is_pinned"`
|
||||||
|
IsPrivateChat bool `json:"is_private_chat"`
|
||||||
|
BurnDuration int32 `json:"burn_duration"`
|
||||||
|
GroupAtType int32 `json:"group_at_type"`
|
||||||
|
AttachedInfo string `json:"attached_info"`
|
||||||
|
Ex string `json:"ex"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CallbackAfterCreateSingleChatConversationsResp struct {
|
||||||
|
CommonCallbackResp
|
||||||
|
}
|
||||||
|
|
||||||
|
type CallbackBeforeCreateGroupChatConversationsReq struct {
|
||||||
|
CallbackCommand `json:"callbackCommand"`
|
||||||
|
OwnerUserID string `json:"owner_user_id"`
|
||||||
|
ConversationID string `json:"conversation_id"`
|
||||||
|
ConversationType int32 `json:"conversation_type"`
|
||||||
|
GroupID string `json:"group_id"`
|
||||||
|
RecvMsgOpt int32 `json:"recv_msg_opt"`
|
||||||
|
IsPinned bool `json:"is_pinned"`
|
||||||
|
IsPrivateChat bool `json:"is_private_chat"`
|
||||||
|
BurnDuration int32 `json:"burn_duration"`
|
||||||
|
GroupAtType int32 `json:"group_at_type"`
|
||||||
|
AttachedInfo string `json:"attached_info"`
|
||||||
|
Ex string `json:"ex"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CallbackBeforeCreateGroupChatConversationsResp struct {
|
||||||
|
CommonCallbackResp
|
||||||
|
RecvMsgOpt *int32 `json:"recv_msg_opt"`
|
||||||
|
IsPinned *bool `json:"is_pinned"`
|
||||||
|
IsPrivateChat *bool `json:"is_private_chat"`
|
||||||
|
BurnDuration *int32 `json:"burn_duration"`
|
||||||
|
GroupAtType *int32 `json:"group_at_type"`
|
||||||
|
AttachedInfo *string `json:"attached_info"`
|
||||||
|
Ex *string `json:"ex"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CallbackAfterCreateGroupChatConversationsReq struct {
|
||||||
|
CallbackCommand `json:"callbackCommand"`
|
||||||
|
OwnerUserID string `json:"owner_user_id"`
|
||||||
|
ConversationID string `json:"conversation_id"`
|
||||||
|
ConversationType int32 `json:"conversation_type"`
|
||||||
|
GroupID string `json:"group_id"`
|
||||||
|
RecvMsgOpt int32 `json:"recv_msg_opt"`
|
||||||
|
IsPinned bool `json:"is_pinned"`
|
||||||
|
IsPrivateChat bool `json:"is_private_chat"`
|
||||||
|
BurnDuration int32 `json:"burn_duration"`
|
||||||
|
GroupAtType int32 `json:"group_at_type"`
|
||||||
|
AttachedInfo string `json:"attached_info"`
|
||||||
|
Ex string `json:"ex"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CallbackAfterCreateGroupChatConversationsResp struct {
|
||||||
|
CommonCallbackResp
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue