复制项目
This commit is contained in:
31
pkg/notification/common_user/common.go
Normal file
31
pkg/notification/common_user/common.go
Normal file
@@ -0,0 +1,31 @@
|
||||
// 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 common_user
|
||||
|
||||
type CommonUser interface {
|
||||
GetNickname() string
|
||||
GetFaceURL() string
|
||||
GetUserID() string
|
||||
GetEx() string
|
||||
GetUserType() int32
|
||||
GetUserFlag() string
|
||||
}
|
||||
|
||||
type CommonGroup interface {
|
||||
GetNickname() string
|
||||
GetFaceURL() string
|
||||
GetGroupID() string
|
||||
GetEx() string
|
||||
}
|
||||
102
pkg/notification/grouphash/grouphash.go
Normal file
102
pkg/notification/grouphash/grouphash.go
Normal file
@@ -0,0 +1,102 @@
|
||||
// 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 grouphash
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
|
||||
"git.imall.cloud/openim/protocol/group"
|
||||
"git.imall.cloud/openim/protocol/sdkws"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
)
|
||||
|
||||
func NewGroupHashFromGroupClient(x group.GroupClient) *GroupHash {
|
||||
return &GroupHash{
|
||||
getGroupAllUserIDs: func(ctx context.Context, groupID string) ([]string, error) {
|
||||
resp, err := x.GetGroupMemberUserIDs(ctx, &group.GetGroupMemberUserIDsReq{GroupID: groupID})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.UserIDs, nil
|
||||
},
|
||||
getGroupMemberInfo: func(ctx context.Context, groupID string, userIDs []string) ([]*sdkws.GroupMemberFullInfo, error) {
|
||||
resp, err := x.GetGroupMembersInfo(ctx, &group.GetGroupMembersInfoReq{GroupID: groupID, UserIDs: userIDs})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.Members, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func NewGroupHashFromGroupServer(x group.GroupServer) *GroupHash {
|
||||
return &GroupHash{
|
||||
getGroupAllUserIDs: func(ctx context.Context, groupID string) ([]string, error) {
|
||||
resp, err := x.GetGroupMemberUserIDs(ctx, &group.GetGroupMemberUserIDsReq{GroupID: groupID})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.UserIDs, nil
|
||||
},
|
||||
getGroupMemberInfo: func(ctx context.Context, groupID string, userIDs []string) ([]*sdkws.GroupMemberFullInfo, error) {
|
||||
resp, err := x.GetGroupMembersInfo(ctx, &group.GetGroupMembersInfoReq{GroupID: groupID, UserIDs: userIDs})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.Members, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type GroupHash struct {
|
||||
getGroupAllUserIDs func(ctx context.Context, groupID string) ([]string, error)
|
||||
getGroupMemberInfo func(ctx context.Context, groupID string, userIDs []string) ([]*sdkws.GroupMemberFullInfo, error)
|
||||
}
|
||||
|
||||
func (gh *GroupHash) GetGroupHash(ctx context.Context, groupID string) (uint64, error) {
|
||||
userIDs, err := gh.getGroupAllUserIDs(ctx, groupID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var members []*sdkws.GroupMemberFullInfo
|
||||
if len(userIDs) > 0 {
|
||||
members, err = gh.getGroupMemberInfo(ctx, groupID, userIDs)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
datautil.Sort(userIDs, true)
|
||||
}
|
||||
memberMap := datautil.SliceToMap(members, func(e *sdkws.GroupMemberFullInfo) string {
|
||||
return e.UserID
|
||||
})
|
||||
res := make([]*sdkws.GroupMemberFullInfo, 0, len(members))
|
||||
for _, userID := range userIDs {
|
||||
member, ok := memberMap[userID]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
member.AppMangerLevel = 0
|
||||
res = append(res, member)
|
||||
}
|
||||
data, err := json.Marshal(res)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
sum := md5.Sum(data)
|
||||
return binary.BigEndian.Uint64(sum[:]), nil
|
||||
}
|
||||
272
pkg/notification/msg.go
Normal file
272
pkg/notification/msg.go
Normal file
@@ -0,0 +1,272 @@
|
||||
// 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 notification
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"git.imall.cloud/openim/open-im-server-deploy/pkg/common/config"
|
||||
"git.imall.cloud/openim/protocol/constant"
|
||||
"git.imall.cloud/openim/protocol/msg"
|
||||
"git.imall.cloud/openim/protocol/sdkws"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/mq/memamq"
|
||||
"github.com/openimsdk/tools/utils/idutil"
|
||||
"github.com/openimsdk/tools/utils/jsonutil"
|
||||
"github.com/openimsdk/tools/utils/timeutil"
|
||||
)
|
||||
|
||||
func newContentTypeConf(conf *config.Notification) map[int32]config.NotificationConfig {
|
||||
return map[int32]config.NotificationConfig{
|
||||
// group
|
||||
constant.GroupCreatedNotification: conf.GroupCreated,
|
||||
constant.GroupInfoSetNotification: conf.GroupInfoSet,
|
||||
constant.JoinGroupApplicationNotification: conf.JoinGroupApplication,
|
||||
constant.MemberQuitNotification: conf.MemberQuit,
|
||||
constant.GroupApplicationAcceptedNotification: conf.GroupApplicationAccepted,
|
||||
constant.GroupApplicationRejectedNotification: conf.GroupApplicationRejected,
|
||||
constant.GroupOwnerTransferredNotification: conf.GroupOwnerTransferred,
|
||||
constant.MemberKickedNotification: conf.MemberKicked,
|
||||
constant.MemberInvitedNotification: conf.MemberInvited,
|
||||
constant.MemberEnterNotification: conf.MemberEnter,
|
||||
constant.GroupDismissedNotification: conf.GroupDismissed,
|
||||
constant.GroupMutedNotification: conf.GroupMuted,
|
||||
constant.GroupCancelMutedNotification: conf.GroupCancelMuted,
|
||||
constant.GroupMemberMutedNotification: conf.GroupMemberMuted,
|
||||
constant.GroupMemberCancelMutedNotification: conf.GroupMemberCancelMuted,
|
||||
constant.GroupMemberInfoSetNotification: conf.GroupMemberInfoSet,
|
||||
constant.GroupMemberSetToAdminNotification: conf.GroupMemberSetToAdmin,
|
||||
constant.GroupMemberSetToOrdinaryUserNotification: conf.GroupMemberSetToOrdinary,
|
||||
constant.GroupInfoSetAnnouncementNotification: conf.GroupInfoSetAnnouncement,
|
||||
constant.GroupInfoSetNameNotification: conf.GroupInfoSetName,
|
||||
// user
|
||||
constant.UserInfoUpdatedNotification: conf.UserInfoUpdated,
|
||||
constant.UserStatusChangeNotification: conf.UserStatusChanged,
|
||||
// friend
|
||||
constant.FriendApplicationNotification: conf.FriendApplicationAdded,
|
||||
constant.FriendApplicationApprovedNotification: conf.FriendApplicationApproved,
|
||||
constant.FriendApplicationRejectedNotification: conf.FriendApplicationRejected,
|
||||
constant.FriendAddedNotification: conf.FriendAdded,
|
||||
constant.FriendDeletedNotification: conf.FriendDeleted,
|
||||
constant.FriendRemarkSetNotification: conf.FriendRemarkSet,
|
||||
constant.BlackAddedNotification: conf.BlackAdded,
|
||||
constant.BlackDeletedNotification: conf.BlackDeleted,
|
||||
constant.FriendInfoUpdatedNotification: conf.FriendInfoUpdated,
|
||||
constant.FriendsInfoUpdateNotification: conf.FriendInfoUpdated, // use the same FriendInfoUpdated
|
||||
// conversation
|
||||
constant.ConversationChangeNotification: conf.ConversationChanged,
|
||||
constant.ConversationUnreadNotification: conf.ConversationChanged,
|
||||
constant.ConversationPrivateChatNotification: conf.ConversationSetPrivate,
|
||||
// msg
|
||||
constant.MsgRevokeNotification: {IsSendMsg: false, ReliabilityLevel: constant.ReliableNotificationNoMsg},
|
||||
constant.HasReadReceipt: {IsSendMsg: false, ReliabilityLevel: constant.ReliableNotificationNoMsg},
|
||||
constant.DeleteMsgsNotification: {IsSendMsg: false, ReliabilityLevel: constant.ReliableNotificationNoMsg},
|
||||
}
|
||||
}
|
||||
|
||||
func newSessionTypeConf() map[int32]int32 {
|
||||
return map[int32]int32{
|
||||
// group
|
||||
constant.GroupCreatedNotification: constant.ReadGroupChatType,
|
||||
constant.GroupInfoSetNotification: constant.ReadGroupChatType,
|
||||
constant.JoinGroupApplicationNotification: constant.SingleChatType,
|
||||
constant.MemberQuitNotification: constant.ReadGroupChatType,
|
||||
constant.GroupApplicationAcceptedNotification: constant.SingleChatType,
|
||||
constant.GroupApplicationRejectedNotification: constant.SingleChatType,
|
||||
constant.GroupOwnerTransferredNotification: constant.ReadGroupChatType,
|
||||
constant.MemberKickedNotification: constant.ReadGroupChatType,
|
||||
constant.MemberInvitedNotification: constant.ReadGroupChatType,
|
||||
constant.MemberEnterNotification: constant.ReadGroupChatType,
|
||||
constant.GroupDismissedNotification: constant.ReadGroupChatType,
|
||||
constant.GroupMutedNotification: constant.ReadGroupChatType,
|
||||
constant.GroupCancelMutedNotification: constant.ReadGroupChatType,
|
||||
constant.GroupMemberMutedNotification: constant.ReadGroupChatType,
|
||||
constant.GroupMemberCancelMutedNotification: constant.ReadGroupChatType,
|
||||
constant.GroupMemberInfoSetNotification: constant.ReadGroupChatType,
|
||||
constant.GroupMemberSetToAdminNotification: constant.ReadGroupChatType,
|
||||
constant.GroupMemberSetToOrdinaryUserNotification: constant.ReadGroupChatType,
|
||||
constant.GroupInfoSetAnnouncementNotification: constant.ReadGroupChatType,
|
||||
constant.GroupInfoSetNameNotification: constant.ReadGroupChatType,
|
||||
// user
|
||||
constant.UserInfoUpdatedNotification: constant.SingleChatType,
|
||||
constant.UserStatusChangeNotification: constant.SingleChatType,
|
||||
// friend
|
||||
constant.FriendApplicationNotification: constant.SingleChatType,
|
||||
constant.FriendApplicationApprovedNotification: constant.SingleChatType,
|
||||
constant.FriendApplicationRejectedNotification: constant.SingleChatType,
|
||||
constant.FriendAddedNotification: constant.SingleChatType,
|
||||
constant.FriendDeletedNotification: constant.SingleChatType,
|
||||
constant.FriendRemarkSetNotification: constant.SingleChatType,
|
||||
constant.BlackAddedNotification: constant.SingleChatType,
|
||||
constant.BlackDeletedNotification: constant.SingleChatType,
|
||||
constant.FriendInfoUpdatedNotification: constant.SingleChatType,
|
||||
constant.FriendsInfoUpdateNotification: constant.SingleChatType,
|
||||
// conversation
|
||||
constant.ConversationChangeNotification: constant.SingleChatType,
|
||||
constant.ConversationUnreadNotification: constant.SingleChatType,
|
||||
constant.ConversationPrivateChatNotification: constant.SingleChatType,
|
||||
// delete
|
||||
constant.DeleteMsgsNotification: constant.SingleChatType,
|
||||
}
|
||||
}
|
||||
|
||||
type NotificationSender struct {
|
||||
contentTypeConf map[int32]config.NotificationConfig
|
||||
sessionTypeConf map[int32]int32
|
||||
sendMsg func(ctx context.Context, req *msg.SendMsgReq) (*msg.SendMsgResp, error)
|
||||
getUserInfo func(ctx context.Context, userID string) (*sdkws.UserInfo, error)
|
||||
queue *memamq.MemoryQueue
|
||||
}
|
||||
|
||||
func WithQueue(queue *memamq.MemoryQueue) NotificationSenderOptions {
|
||||
return func(s *NotificationSender) {
|
||||
s.queue = queue
|
||||
}
|
||||
}
|
||||
|
||||
type NotificationSenderOptions func(*NotificationSender)
|
||||
|
||||
func WithLocalSendMsg(sendMsg func(ctx context.Context, req *msg.SendMsgReq) (*msg.SendMsgResp, error)) NotificationSenderOptions {
|
||||
return func(s *NotificationSender) {
|
||||
s.sendMsg = sendMsg
|
||||
}
|
||||
}
|
||||
|
||||
func WithRpcClient(sendMsg func(ctx context.Context, req *msg.SendMsgReq) (*msg.SendMsgResp, error)) NotificationSenderOptions {
|
||||
return func(s *NotificationSender) {
|
||||
s.sendMsg = func(ctx context.Context, req *msg.SendMsgReq) (*msg.SendMsgResp, error) {
|
||||
return sendMsg(ctx, req)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func WithUserRpcClient(getUserInfo func(ctx context.Context, userID string) (*sdkws.UserInfo, error)) NotificationSenderOptions {
|
||||
return func(s *NotificationSender) {
|
||||
s.getUserInfo = getUserInfo
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
notificationWorkerCount = 16
|
||||
notificationBufferSize = 1024 * 1024 * 2
|
||||
)
|
||||
|
||||
func NewNotificationSender(conf *config.Notification, opts ...NotificationSenderOptions) *NotificationSender {
|
||||
notificationSender := &NotificationSender{contentTypeConf: newContentTypeConf(conf), sessionTypeConf: newSessionTypeConf()}
|
||||
for _, opt := range opts {
|
||||
opt(notificationSender)
|
||||
}
|
||||
if notificationSender.queue == nil {
|
||||
notificationSender.queue = memamq.NewMemoryQueue(notificationWorkerCount, notificationBufferSize)
|
||||
}
|
||||
return notificationSender
|
||||
}
|
||||
|
||||
type notificationOpt struct {
|
||||
RpcGetUsername bool
|
||||
SendMessage *bool
|
||||
}
|
||||
|
||||
type NotificationOptions func(*notificationOpt)
|
||||
|
||||
func WithRpcGetUserName() NotificationOptions {
|
||||
return func(opt *notificationOpt) {
|
||||
opt.RpcGetUsername = true
|
||||
}
|
||||
}
|
||||
func WithSendMessage(sendMessage *bool) NotificationOptions {
|
||||
return func(opt *notificationOpt) {
|
||||
opt.SendMessage = sendMessage
|
||||
}
|
||||
}
|
||||
|
||||
func (s *NotificationSender) send(ctx context.Context, sendID, recvID string, contentType, sessionType int32, m proto.Message, opts ...NotificationOptions) {
|
||||
ctx = context.WithoutCancel(ctx)
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Second*time.Duration(5))
|
||||
defer cancel()
|
||||
n := sdkws.NotificationElem{Detail: jsonutil.StructToJsonString(m)}
|
||||
content, err := json.Marshal(&n)
|
||||
if err != nil {
|
||||
log.ZWarn(ctx, "json.Marshal failed", err, "sendID", sendID, "recvID", recvID, "contentType", contentType, "msg", jsonutil.StructToJsonString(m))
|
||||
return
|
||||
}
|
||||
notificationOpt := ¬ificationOpt{}
|
||||
for _, opt := range opts {
|
||||
opt(notificationOpt)
|
||||
}
|
||||
var req msg.SendMsgReq
|
||||
var msg sdkws.MsgData
|
||||
var userInfo *sdkws.UserInfo
|
||||
if notificationOpt.RpcGetUsername && s.getUserInfo != nil {
|
||||
userInfo, err = s.getUserInfo(ctx, sendID)
|
||||
if err != nil {
|
||||
log.ZWarn(ctx, "getUserInfo failed", err, "sendID", sendID)
|
||||
return
|
||||
}
|
||||
msg.SenderNickname = userInfo.Nickname
|
||||
msg.SenderFaceURL = userInfo.FaceURL
|
||||
}
|
||||
var offlineInfo sdkws.OfflinePushInfo
|
||||
msg.SendID = sendID
|
||||
msg.RecvID = recvID
|
||||
msg.Content = content
|
||||
msg.MsgFrom = constant.SysMsgType
|
||||
msg.ContentType = contentType
|
||||
msg.SessionType = sessionType
|
||||
if msg.SessionType == constant.ReadGroupChatType {
|
||||
msg.GroupID = recvID
|
||||
}
|
||||
msg.CreateTime = timeutil.GetCurrentTimestampByMill()
|
||||
msg.ClientMsgID = idutil.GetMsgIDByMD5(sendID)
|
||||
optionsConfig := s.contentTypeConf[contentType]
|
||||
if sendID == recvID && contentType == constant.HasReadReceipt {
|
||||
optionsConfig.ReliabilityLevel = constant.UnreliableNotification
|
||||
}
|
||||
options := config.GetOptionsByNotification(optionsConfig, notificationOpt.SendMessage)
|
||||
s.SetOptionsByContentType(ctx, options, contentType)
|
||||
msg.Options = options
|
||||
// fill Notification OfflinePush by config
|
||||
offlineInfo.Title = optionsConfig.OfflinePush.Title
|
||||
offlineInfo.Desc = optionsConfig.OfflinePush.Desc
|
||||
offlineInfo.Ex = optionsConfig.OfflinePush.Ext
|
||||
msg.OfflinePushInfo = &offlineInfo
|
||||
req.MsgData = &msg
|
||||
_, err = s.sendMsg(ctx, &req)
|
||||
if err != nil {
|
||||
log.ZWarn(ctx, "SendMsg failed", err, "req", req.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (s *NotificationSender) NotificationWithSessionType(ctx context.Context, sendID, recvID string, contentType, sessionType int32, m proto.Message, opts ...NotificationOptions) {
|
||||
if err := s.queue.Push(func() { s.send(ctx, sendID, recvID, contentType, sessionType, m, opts...) }); err != nil {
|
||||
log.ZWarn(ctx, "Push to queue failed", err, "sendID", sendID, "recvID", recvID, "msg", jsonutil.StructToJsonString(m))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *NotificationSender) Notification(ctx context.Context, sendID, recvID string, contentType int32, m proto.Message, opts ...NotificationOptions) {
|
||||
s.NotificationWithSessionType(ctx, sendID, recvID, contentType, s.sessionTypeConf[contentType], m, opts...)
|
||||
}
|
||||
|
||||
func (s *NotificationSender) SetOptionsByContentType(_ context.Context, options map[string]bool, contentType int32) {
|
||||
switch contentType {
|
||||
case constant.UserStatusChangeNotification:
|
||||
options[constant.IsSenderSync] = false
|
||||
default:
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user