feat: Optimizing RPC call (#2993)

* pb

* fix: Modifying other fields while setting IsPrivateChat does not take effect

* fix: quote message error revoke

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* rpc client

* rpc client

* rpc client

* rpc client

* rpc client

* rpc client

* rpc client

* rpc client
This commit is contained in:
chao
2024-12-24 10:51:38 +08:00
committed by OpenIM-Robot
parent b26b0a422c
commit de42eb1f11
70 changed files with 1462 additions and 785 deletions
+74
View File
@@ -0,0 +1,74 @@
// 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 rpcclient
import (
"context"
"github.com/openimsdk/protocol/auth"
"github.com/openimsdk/tools/discovery"
"github.com/openimsdk/tools/system/program"
"google.golang.org/grpc"
)
func NewAuth(discov discovery.SvcDiscoveryRegistry, rpcRegisterName string) *Auth {
conn, err := discov.GetConn(context.Background(), rpcRegisterName)
if err != nil {
program.ExitWithError(err)
}
client := auth.NewAuthClient(conn)
return &Auth{discov: discov, conn: conn, Client: client}
}
type Auth struct {
conn grpc.ClientConnInterface
Client auth.AuthClient
discov discovery.SvcDiscoveryRegistry
}
func (a *Auth) ParseToken(ctx context.Context, token string) (*auth.ParseTokenResp, error) {
req := auth.ParseTokenReq{
Token: token,
}
resp, err := a.Client.ParseToken(ctx, &req)
if err != nil {
return nil, err
}
return resp, err
}
func (a *Auth) InvalidateToken(ctx context.Context, preservedToken, userID string, platformID int) (*auth.InvalidateTokenResp, error) {
req := auth.InvalidateTokenReq{
PreservedToken: preservedToken,
UserID: userID,
PlatformID: int32(platformID),
}
resp, err := a.Client.InvalidateToken(ctx, &req)
if err != nil {
return nil, err
}
return resp, err
}
func (a *Auth) KickTokens(ctx context.Context, tokens []string) (*auth.KickTokensResp, error) {
req := auth.KickTokensReq{
Tokens: tokens,
}
resp, err := a.Client.KickTokens(ctx, &req)
if err != nil {
return nil, err
}
return resp, err
}
+29
View File
@@ -0,0 +1,29 @@
// 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
}
type CommonGroup interface {
GetNickname() string
GetFaceURL() string
GetGroupID() string
GetEx() string
}
+161
View File
@@ -0,0 +1,161 @@
// 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 rpcclient
import (
"context"
"fmt"
pbconversation "github.com/openimsdk/protocol/conversation"
"github.com/openimsdk/tools/discovery"
"github.com/openimsdk/tools/errs"
"github.com/openimsdk/tools/system/program"
"google.golang.org/grpc"
)
type Conversation struct {
Client pbconversation.ConversationClient
conn grpc.ClientConnInterface
discov discovery.SvcDiscoveryRegistry
}
func NewConversation(discov discovery.SvcDiscoveryRegistry, rpcRegisterName string) *Conversation {
conn, err := discov.GetConn(context.Background(), rpcRegisterName)
if err != nil {
program.ExitWithError(err)
}
client := pbconversation.NewConversationClient(conn)
return &Conversation{discov: discov, conn: conn, Client: client}
}
type ConversationRpcClient Conversation
func NewConversationRpcClient(discov discovery.SvcDiscoveryRegistry, rpcRegisterName string) ConversationRpcClient {
return ConversationRpcClient(*NewConversation(discov, rpcRegisterName))
}
func (c *ConversationRpcClient) GetSingleConversationRecvMsgOpt(ctx context.Context, userID, conversationID string) (int32, error) {
var req pbconversation.GetConversationReq
req.OwnerUserID = userID
req.ConversationID = conversationID
conversation, err := c.Client.GetConversation(ctx, &req)
if err != nil {
return 0, err
}
return conversation.GetConversation().RecvMsgOpt, err
}
func (c *ConversationRpcClient) SingleChatFirstCreateConversation(ctx context.Context, recvID, sendID,
conversationID string, conversationType int32) error {
_, err := c.Client.CreateSingleChatConversations(ctx,
&pbconversation.CreateSingleChatConversationsReq{
RecvID: recvID, SendID: sendID, ConversationID: conversationID,
ConversationType: conversationType,
})
return err
}
func (c *ConversationRpcClient) GroupChatFirstCreateConversation(ctx context.Context, groupID string, userIDs []string) error {
_, err := c.Client.CreateGroupChatConversations(ctx, &pbconversation.CreateGroupChatConversationsReq{UserIDs: userIDs, GroupID: groupID})
return err
}
func (c *ConversationRpcClient) SetConversationMaxSeq(ctx context.Context, ownerUserIDs []string, conversationID string, maxSeq int64) error {
_, err := c.Client.SetConversationMaxSeq(ctx, &pbconversation.SetConversationMaxSeqReq{OwnerUserID: ownerUserIDs, ConversationID: conversationID, MaxSeq: maxSeq})
return err
}
func (c *ConversationRpcClient) SetConversationMinSeq(ctx context.Context, ownerUserIDs []string, conversationID string, minSeq int64) error {
_, err := c.Client.SetConversationMinSeq(ctx, &pbconversation.SetConversationMinSeqReq{OwnerUserID: ownerUserIDs, ConversationID: conversationID, MinSeq: minSeq})
return err
}
func (c *ConversationRpcClient) SetConversations(ctx context.Context, userIDs []string, conversation *pbconversation.ConversationReq) error {
_, err := c.Client.SetConversations(ctx, &pbconversation.SetConversationsReq{UserIDs: userIDs, Conversation: conversation})
return err
}
func (c *ConversationRpcClient) UpdateConversation(ctx context.Context, conversation *pbconversation.UpdateConversationReq) error {
_, err := c.Client.UpdateConversation(ctx, conversation)
return err
}
func (c *ConversationRpcClient) GetConversationIDs(ctx context.Context, ownerUserID string) ([]string, error) {
resp, err := c.Client.GetConversationIDs(ctx, &pbconversation.GetConversationIDsReq{UserID: ownerUserID})
if err != nil {
return nil, err
}
return resp.ConversationIDs, nil
}
func (c *ConversationRpcClient) GetConversation(ctx context.Context, ownerUserID, conversationID string) (*pbconversation.Conversation, error) {
resp, err := c.Client.GetConversation(ctx, &pbconversation.GetConversationReq{OwnerUserID: ownerUserID, ConversationID: conversationID})
if err != nil {
return nil, err
}
return resp.Conversation, nil
}
func (c *ConversationRpcClient) GetConversationsByConversationID(ctx context.Context, conversationIDs []string) ([]*pbconversation.Conversation, error) {
if len(conversationIDs) == 0 {
return nil, nil
}
resp, err := c.Client.GetConversationsByConversationID(ctx, &pbconversation.GetConversationsByConversationIDReq{ConversationIDs: conversationIDs})
if err != nil {
return nil, err
}
if len(resp.Conversations) == 0 {
return nil, errs.ErrRecordNotFound.WrapMsg(fmt.Sprintf("conversationIDs: %v not found", conversationIDs))
}
return resp.Conversations, nil
}
func (c *ConversationRpcClient) GetConversationOfflinePushUserIDs(ctx context.Context, conversationID string, userIDs []string) ([]string, error) {
resp, err := c.Client.GetConversationOfflinePushUserIDs(ctx, &pbconversation.GetConversationOfflinePushUserIDsReq{ConversationID: conversationID, UserIDs: userIDs})
if err != nil {
return nil, err
}
return resp.UserIDs, nil
}
func (c *ConversationRpcClient) GetConversations(ctx context.Context, ownerUserID string, conversationIDs []string) ([]*pbconversation.Conversation, error) {
if len(conversationIDs) == 0 {
return nil, nil
}
resp, err := c.Client.GetConversations(
ctx,
&pbconversation.GetConversationsReq{OwnerUserID: ownerUserID, ConversationIDs: conversationIDs},
)
if err != nil {
return nil, err
}
return resp.Conversations, nil
}
func (c *ConversationRpcClient) GetConversationNotReceiveMessageUserIDs(ctx context.Context, conversationID string) ([]string, error) {
resp, err := c.Client.GetConversationNotReceiveMessageUserIDs(ctx, &pbconversation.GetConversationNotReceiveMessageUserIDsReq{ConversationID: conversationID})
if err != nil {
return nil, err
}
return resp.UserIDs, nil
}
func (c *ConversationRpcClient) GetConversationsNeedClearMsg(ctx context.Context) ([]*pbconversation.Conversation, error) {
resp, err := c.Client.GetConversationsNeedClearMsg(ctx, &pbconversation.GetConversationsNeedClearMsgReq{})
if err != nil {
return nil, err
}
return resp.Conversations, nil
}
+87
View File
@@ -0,0 +1,87 @@
// 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 rpcclient
import (
"context"
"github.com/openimsdk/protocol/relation"
sdkws "github.com/openimsdk/protocol/sdkws"
"github.com/openimsdk/tools/discovery"
"github.com/openimsdk/tools/system/program"
"google.golang.org/grpc"
)
type Friend struct {
conn grpc.ClientConnInterface
Client relation.FriendClient
discov discovery.SvcDiscoveryRegistry
}
func NewFriend(discov discovery.SvcDiscoveryRegistry, rpcRegisterName string) *Friend {
conn, err := discov.GetConn(context.Background(), rpcRegisterName)
if err != nil {
program.ExitWithError(err)
}
client := relation.NewFriendClient(conn)
return &Friend{discov: discov, conn: conn, Client: client}
}
type FriendRpcClient Friend
func NewFriendRpcClient(discov discovery.SvcDiscoveryRegistry, rpcRegisterName string) FriendRpcClient {
return FriendRpcClient(*NewFriend(discov, rpcRegisterName))
}
func (f *FriendRpcClient) GetFriendsInfo(
ctx context.Context,
ownerUserID, relationUserID string,
) (resp *sdkws.FriendInfo, err error) {
r, err := f.Client.GetDesignatedFriends(
ctx,
&relation.GetDesignatedFriendsReq{OwnerUserID: ownerUserID, FriendUserIDs: []string{relationUserID}},
)
if err != nil {
return nil, err
}
resp = r.FriendsInfo[0]
return
}
// possibleFriendUserID Is PossibleFriendUserId's relations.
func (f *FriendRpcClient) IsFriend(ctx context.Context, possibleFriendUserID, userID string) (bool, error) {
resp, err := f.Client.IsFriend(ctx, &relation.IsFriendReq{UserID1: userID, UserID2: possibleFriendUserID})
if err != nil {
return false, err
}
return resp.InUser1Friends, nil
}
func (f *FriendRpcClient) GetFriendIDs(ctx context.Context, ownerUserID string) (relationIDs []string, err error) {
req := relation.GetFriendIDsReq{UserID: ownerUserID}
resp, err := f.Client.GetFriendIDs(ctx, &req)
if err != nil {
return nil, err
}
return resp.FriendIDs, nil
}
func (f *FriendRpcClient) IsBlack(ctx context.Context, possibleBlackUserID, userID string) (bool, error) {
r, err := f.Client.IsBlack(ctx, &relation.IsBlackReq{UserID1: possibleBlackUserID, UserID2: userID})
if err != nil {
return false, err
}
return r.InUser2Blacks, nil
}
+205
View File
@@ -0,0 +1,205 @@
// 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 rpcclient
import (
"context"
"strings"
"github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
"github.com/openimsdk/protocol/constant"
"github.com/openimsdk/protocol/group"
"github.com/openimsdk/protocol/sdkws"
"github.com/openimsdk/tools/discovery"
"github.com/openimsdk/tools/system/program"
"github.com/openimsdk/tools/utils/datautil"
)
type Group struct {
Client group.GroupClient
discov discovery.SvcDiscoveryRegistry
}
func NewGroup(discov discovery.SvcDiscoveryRegistry, rpcRegisterName string) *Group {
conn, err := discov.GetConn(context.Background(), rpcRegisterName)
if err != nil {
program.ExitWithError(err)
}
client := group.NewGroupClient(conn)
return &Group{discov: discov, Client: client}
}
type GroupRpcClient Group
func NewGroupRpcClient(discov discovery.SvcDiscoveryRegistry, rpcRegisterName string) GroupRpcClient {
return GroupRpcClient(*NewGroup(discov, rpcRegisterName))
}
func (g *GroupRpcClient) GetGroupInfos(ctx context.Context, groupIDs []string, complete bool) ([]*sdkws.GroupInfo, error) {
resp, err := g.Client.GetGroupsInfo(ctx, &group.GetGroupsInfoReq{
GroupIDs: groupIDs,
})
if err != nil {
return nil, err
}
if complete {
if ids := datautil.Single(groupIDs, datautil.Slice(resp.GroupInfos, func(e *sdkws.GroupInfo) string {
return e.GroupID
})); len(ids) > 0 {
return nil, servererrs.ErrGroupIDNotFound.WrapMsg(strings.Join(ids, ","))
}
}
return resp.GroupInfos, nil
}
func (g *GroupRpcClient) GetGroupInfo(ctx context.Context, groupID string) (*sdkws.GroupInfo, error) {
groups, err := g.GetGroupInfos(ctx, []string{groupID}, true)
if err != nil {
return nil, err
}
return groups[0], nil
}
func (g *GroupRpcClient) GetGroupInfoMap(
ctx context.Context,
groupIDs []string,
complete bool,
) (map[string]*sdkws.GroupInfo, error) {
groups, err := g.GetGroupInfos(ctx, groupIDs, complete)
if err != nil {
return nil, err
}
return datautil.SliceToMap(groups, func(e *sdkws.GroupInfo) string {
return e.GroupID
}), nil
}
func (g *GroupRpcClient) GetGroupMemberInfos(
ctx context.Context,
groupID string,
userIDs []string,
complete bool,
) ([]*sdkws.GroupMemberFullInfo, error) {
resp, err := g.Client.GetGroupMembersInfo(ctx, &group.GetGroupMembersInfoReq{
GroupID: groupID,
UserIDs: userIDs,
})
if err != nil {
return nil, err
}
if complete {
if ids := datautil.Single(userIDs, datautil.Slice(resp.Members, func(e *sdkws.GroupMemberFullInfo) string {
return e.UserID
})); len(ids) > 0 {
return nil, servererrs.ErrNotInGroupYet.WrapMsg(strings.Join(ids, ","))
}
}
return resp.Members, nil
}
func (g *GroupRpcClient) GetGroupMemberInfo(
ctx context.Context,
groupID string,
userID string,
) (*sdkws.GroupMemberFullInfo, error) {
members, err := g.GetGroupMemberInfos(ctx, groupID, []string{userID}, true)
if err != nil {
return nil, err
}
return members[0], nil
}
func (g *GroupRpcClient) GetGroupMemberInfoMap(
ctx context.Context,
groupID string,
userIDs []string,
complete bool,
) (map[string]*sdkws.GroupMemberFullInfo, error) {
members, err := g.GetGroupMemberInfos(ctx, groupID, userIDs, true)
if err != nil {
return nil, err
}
return datautil.SliceToMap(members, func(e *sdkws.GroupMemberFullInfo) string {
return e.UserID
}), nil
}
func (g *GroupRpcClient) GetOwnerAndAdminInfos(
ctx context.Context,
groupID string,
) ([]*sdkws.GroupMemberFullInfo, error) {
resp, err := g.Client.GetGroupMemberRoleLevel(ctx, &group.GetGroupMemberRoleLevelReq{
GroupID: groupID,
RoleLevels: []int32{constant.GroupOwner, constant.GroupAdmin},
})
if err != nil {
return nil, err
}
return resp.Members, nil
}
func (g *GroupRpcClient) GetOwnerInfo(ctx context.Context, groupID string) (*sdkws.GroupMemberFullInfo, error) {
resp, err := g.Client.GetGroupMemberRoleLevel(ctx, &group.GetGroupMemberRoleLevelReq{
GroupID: groupID,
RoleLevels: []int32{constant.GroupOwner},
})
return resp.Members[0], err
}
func (g *GroupRpcClient) GetGroupMemberIDs(ctx context.Context, groupID string) ([]string, error) {
resp, err := g.Client.GetGroupMemberUserIDs(ctx, &group.GetGroupMemberUserIDsReq{
GroupID: groupID,
})
if err != nil {
return nil, err
}
return resp.UserIDs, nil
}
func (g *GroupRpcClient) GetGroupInfoCache(ctx context.Context, groupID string) (*sdkws.GroupInfo, error) {
resp, err := g.Client.GetGroupInfoCache(ctx, &group.GetGroupInfoCacheReq{
GroupID: groupID,
})
if err != nil {
return nil, err
}
return resp.GroupInfo, nil
}
func (g *GroupRpcClient) GetGroupMemberCache(ctx context.Context, groupID string, groupMemberID string) (*sdkws.GroupMemberFullInfo, error) {
resp, err := g.Client.GetGroupMemberCache(ctx, &group.GetGroupMemberCacheReq{
GroupID: groupID,
GroupMemberID: groupMemberID,
})
if err != nil {
return nil, err
}
return resp.Member, nil
}
func (g *GroupRpcClient) DismissGroup(ctx context.Context, groupID string) error {
_, err := g.Client.DismissGroup(ctx, &group.DismissGroupReq{
GroupID: groupID,
DeleteMember: true,
})
return err
}
func (g *GroupRpcClient) NotificationUserInfoUpdate(ctx context.Context, userID string) error {
_, err := g.Client.NotificationUserInfoUpdate(ctx, &group.NotificationUserInfoUpdateReq{
UserID: userID,
})
return err
}
+102
View 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"
"github.com/openimsdk/protocol/group"
"github.com/openimsdk/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
}
+390
View File
@@ -0,0 +1,390 @@
// 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 rpcclient
import (
"context"
"encoding/json"
"time"
"google.golang.org/grpc"
"google.golang.org/protobuf/proto"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/protocol/constant"
"github.com/openimsdk/protocol/msg"
"github.com/openimsdk/protocol/sdkws"
"github.com/openimsdk/tools/discovery"
"github.com/openimsdk/tools/log"
"github.com/openimsdk/tools/mq/memamq"
"github.com/openimsdk/tools/system/program"
"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 Message struct {
conn grpc.ClientConnInterface
Client msg.MsgClient
discov discovery.SvcDiscoveryRegistry
}
func NewMessage(discov discovery.SvcDiscoveryRegistry, rpcRegisterName string) *Message {
conn, err := discov.GetConn(context.Background(), rpcRegisterName)
if err != nil {
program.ExitWithError(err)
}
client := msg.NewMsgClient(conn)
return &Message{discov: discov, conn: conn, Client: client}
}
type MessageRpcClient Message
func NewMessageRpcClient(discov discovery.SvcDiscoveryRegistry, rpcRegisterName string) MessageRpcClient {
return MessageRpcClient(*NewMessage(discov, rpcRegisterName))
}
// SendMsg sends a message through the gRPC client and returns the response.
// It wraps any encountered error for better error handling and context understanding.
func (m *MessageRpcClient) SendMsg(ctx context.Context, req *msg.SendMsgReq) (*msg.SendMsgResp, error) {
resp, err := m.Client.SendMsg(ctx, req)
if err != nil {
return nil, err
}
return resp, nil
}
// SetUserConversationsMinSeq set min seq
func (m *MessageRpcClient) SetUserConversationsMinSeq(ctx context.Context, req *msg.SetUserConversationsMinSeqReq) (*msg.SetUserConversationsMinSeqResp, error) {
resp, err := m.Client.SetUserConversationsMinSeq(ctx, req)
if err != nil {
return nil, err
}
return resp, nil
}
// GetMaxSeq retrieves the maximum sequence number from the gRPC client.
// Errors during the gRPC call are wrapped to provide additional context.
func (m *MessageRpcClient) GetMaxSeq(ctx context.Context, req *sdkws.GetMaxSeqReq) (*sdkws.GetMaxSeqResp, error) {
resp, err := m.Client.GetMaxSeq(ctx, req)
if err != nil {
return nil, err
}
return resp, nil
}
func (m *MessageRpcClient) GetMaxSeqs(ctx context.Context, conversationIDs []string) (map[string]int64, error) {
log.ZDebug(ctx, "GetMaxSeqs", "conversationIDs", conversationIDs)
resp, err := m.Client.GetMaxSeqs(ctx, &msg.GetMaxSeqsReq{
ConversationIDs: conversationIDs,
})
if err != nil {
return nil, err
}
return resp.MaxSeqs, err
}
func (m *MessageRpcClient) GetHasReadSeqs(ctx context.Context, userID string, conversationIDs []string) (map[string]int64, error) {
resp, err := m.Client.GetHasReadSeqs(ctx, &msg.GetHasReadSeqsReq{
UserID: userID,
ConversationIDs: conversationIDs,
})
if err != nil {
return nil, err
}
return resp.MaxSeqs, err
}
func (m *MessageRpcClient) GetMsgByConversationIDs(ctx context.Context, docIDs []string, seqs map[string]int64) (map[string]*sdkws.MsgData, error) {
resp, err := m.Client.GetMsgByConversationIDs(ctx, &msg.GetMsgByConversationIDsReq{
ConversationIDs: docIDs,
MaxSeqs: seqs,
})
if err != nil {
return nil, err
}
return resp.MsgDatas, err
}
// PullMessageBySeqList retrieves messages by their sequence numbers using the gRPC client.
// It directly forwards the request to the gRPC client and returns the response along with any error encountered.
func (m *MessageRpcClient) PullMessageBySeqList(ctx context.Context, req *sdkws.PullMessageBySeqsReq) (*sdkws.PullMessageBySeqsResp, error) {
resp, err := m.Client.PullMessageBySeqs(ctx, req)
if err != nil {
// Wrap the error to provide more context if the gRPC call fails.
return nil, err
}
return resp, nil
}
func (m *MessageRpcClient) GetConversationsHasReadAndMaxSeq(ctx context.Context, req *msg.GetConversationsHasReadAndMaxSeqReq) (*msg.GetConversationsHasReadAndMaxSeqResp, error) {
resp, err := m.Client.GetConversationsHasReadAndMaxSeq(ctx, req)
if err != nil {
// Wrap the error to provide more context if the gRPC call fails.
return nil, err
}
return resp, nil
}
func (m *MessageRpcClient) GetSeqMessage(ctx context.Context, req *msg.GetSeqMessageReq) (*msg.GetSeqMessageResp, error) {
return m.Client.GetSeqMessage(ctx, req)
}
func (m *MessageRpcClient) GetConversationMaxSeq(ctx context.Context, conversationID string) (int64, error) {
resp, err := m.Client.GetConversationMaxSeq(ctx, &msg.GetConversationMaxSeqReq{ConversationID: conversationID})
if err != nil {
return 0, err
}
return resp.MaxSeq, nil
}
func (m *MessageRpcClient) DestructMsgs(ctx context.Context, ts int64) error {
_, err := m.Client.DestructMsgs(ctx, &msg.DestructMsgsReq{Timestamp: ts})
return err
}
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 {
WithRpcGetUsername bool
}
type NotificationOptions func(*notificationOpt)
func WithRpcGetUserName() NotificationOptions {
return func(opt *notificationOpt) {
opt.WithRpcGetUsername = true
}
}
func (s *NotificationSender) send(ctx context.Context, sendID, recvID string, contentType, sessionType int32, m proto.Message, opts ...NotificationOptions) {
//ctx = mcontext.WithMustInfoCtx([]string{mcontext.GetOperationID(ctx), mcontext.GetOpUserID(ctx), mcontext.GetOpUserPlatform(ctx), mcontext.GetConnID(ctx)})
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 := &notificationOpt{}
for _, opt := range opts {
opt(notificationOpt)
}
var req msg.SendMsgReq
var msg sdkws.MsgData
var userInfo *sdkws.UserInfo
if notificationOpt.WithRpcGetUsername && 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)
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:
}
}
+52
View File
@@ -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 rpcclient
import (
"context"
"github.com/openimsdk/protocol/push"
"github.com/openimsdk/tools/discovery"
"github.com/openimsdk/tools/system/program"
"google.golang.org/grpc"
)
type Push struct {
conn grpc.ClientConnInterface
Client push.PushMsgServiceClient
discov discovery.SvcDiscoveryRegistry
}
func NewPush(discov discovery.SvcDiscoveryRegistry, rpcRegisterName string) *Push {
conn, err := discov.GetConn(context.Background(), rpcRegisterName)
if err != nil {
program.ExitWithError(err)
}
return &Push{
discov: discov,
conn: conn,
Client: push.NewPushMsgServiceClient(conn),
}
}
type PushRpcClient Push
func NewPushRpcClient(discov discovery.SvcDiscoveryRegistry, rpcRegisterName string) PushRpcClient {
return PushRpcClient(*NewPush(discov, rpcRegisterName))
}
func (p *PushRpcClient) DelUserPushToken(ctx context.Context, req *push.DelUserPushTokenReq) (*push.DelUserPushTokenResp, error) {
return p.Client.DelUserPushToken(ctx, req)
}
+47
View File
@@ -0,0 +1,47 @@
// 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 rpcclient
import (
"context"
"github.com/openimsdk/protocol/third"
"github.com/openimsdk/tools/discovery"
"github.com/openimsdk/tools/system/program"
"google.golang.org/grpc"
)
type Third struct {
conn grpc.ClientConnInterface
Client third.ThirdClient
discov discovery.SvcDiscoveryRegistry
GrafanaUrl string
}
func NewThird(discov discovery.SvcDiscoveryRegistry, rpcRegisterName, grafanaUrl string) *Third {
conn, err := discov.GetConn(context.Background(), rpcRegisterName)
if err != nil {
program.ExitWithError(err)
}
client := third.NewThirdClient(conn)
if err != nil {
program.ExitWithError(err)
}
return &Third{discov: discov, Client: client, conn: conn, GrafanaUrl: grafanaUrl}
}
func (t *Third) DeleteOutdatedData(ctx context.Context, expires int64) error {
_, err := t.Client.DeleteOutdatedData(ctx, &third.DeleteOutdatedDataReq{ExpireTime: expires})
return err
}