用户设置

pull/3727/head
hawklin2017 2 months ago
parent 42064d31a7
commit e9d75d8e78

@ -153,13 +153,12 @@ func (m *msgServer) sendMsgNotification(ctx context.Context, req *pbmsg.SendMsgR
}
func (m *msgServer) sendMsgSingleChat(ctx context.Context, req *pbmsg.SendMsgReq) (resp *pbmsg.SendMsgResp, err error) {
if err := m.messageVerification(ctx, req); err != nil {
return nil, err
}
isSend := true
isNotification := msgprocessor.IsNotificationByMsg(req.MsgData)
log.ZInfo(ctx, "sendMsgSingleChat", "isNotification", isNotification, "msgdata", req.MsgData)
isSend := true
if !isNotification {
log.ZInfo(ctx, "sendMsgSingleChat", "isNotification", isNotification, "msgdata", req.MsgData)
// 非通知类消息:执行发送权限校验 + 接收偏好校验(含 blacklist / MsgReceiveSetting / webhook / FriendVerify / globalOpt / convOpt
isSend, err = m.modifyMessageByUserMessageReceiveOpt(
ctx,
req.MsgData.RecvID,
@ -174,23 +173,21 @@ func (m *msgServer) sendMsgSingleChat(ctx context.Context, req *pbmsg.SendMsgReq
if !isSend {
prommetrics.SingleChatMsgProcessFailedCounter.Inc()
return nil, nil
} else {
if err := m.webhookBeforeMsgModify(ctx, &m.config.WebhooksConfig.BeforeMsgModify, req); err != nil {
return nil, err
}
log.ZInfo(ctx, "sendMsgSingleChat", "isNotification", isNotification, "msgdata", req.MsgData)
}
if err := m.MsgDatabase.MsgToMQ(ctx, conversationutil.GenConversationUniqueKeyForSingle(req.MsgData.SendID, req.MsgData.RecvID), req.MsgData); err != nil {
prommetrics.SingleChatMsgProcessFailedCounter.Inc()
return nil, err
}
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,
}, nil
if err := m.webhookBeforeMsgModify(ctx, &m.config.WebhooksConfig.BeforeMsgModify, req); err != nil {
return nil, err
}
log.ZInfo(ctx, "sendMsgSingleChat after modify", "isNotification", isNotification, "msgdata", req.MsgData)
if err := m.MsgDatabase.MsgToMQ(ctx, conversationutil.GenConversationUniqueKeyForSingle(req.MsgData.SendID, req.MsgData.RecvID), req.MsgData); err != nil {
prommetrics.SingleChatMsgProcessFailedCounter.Inc()
return nil, err
}
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,
}, nil
}

@ -16,18 +16,19 @@ 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/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/utils/datautil"
"github.com/openimsdk/tools/utils/encrypt"
"github.com/openimsdk/tools/utils/timeutil"
)
var ExcludeContentType = []int{constant.HasReadReceipt}
@ -52,61 +53,14 @@ type MessageRevoked struct {
func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgReq) error {
switch data.MsgData.SessionType {
case constant.SingleChatType:
if datautil.Contain(data.MsgData.SendID, m.config.Share.IMAdminUserID...) {
return nil
}
if data.MsgData.ContentType <= constant.NotificationEnd &&
data.MsgData.ContentType >= constant.NotificationBegin {
return nil
}
// 先做本地轻量级拦截(黑名单 + 消息接收权限),避免不必要的 webhook 触发
black, err := m.FriendLocalCache.IsBlack(ctx, data.MsgData.SendID, data.MsgData.RecvID)
if err != nil {
return err
}
if black {
return servererrs.ErrBlockedByPeer.Wrap()
}
// 校验接收方消息接收权限MsgReceiveSetting
// 0=所有人可发送1=仅好友可发送2=所有人不可发送
recvUserInfo, err := m.UserLocalCache.GetUserInfo(ctx, data.MsgData.RecvID)
if err != nil {
return err
}
switch recvUserInfo.MsgReceiveSetting {
case 2: // MsgReceiveSettingNobody
return servererrs.ErrMsgReceiveNotAllowed.Wrap()
case 1: // MsgReceiveSettingFriends
isFriend, err := m.FriendLocalCache.IsFriend(ctx, data.MsgData.RecvID, data.MsgData.SendID)
if err != nil {
return err
}
if !isFriend {
return servererrs.ErrMsgReceiveNotAllowed.Wrap()
}
// 已确认是好友,触发 webhook 后放行,不做 FriendVerify 冗余查询
if err := m.webhookBeforeSendSingleMsg(ctx, &m.config.WebhooksConfig.BeforeSendSingleMsg, data); err != nil {
return err
}
return nil
}
// MsgReceiveSetting==0所有人可发触发 webhook再按全局 FriendVerify 兜底
if err := m.webhookBeforeSendSingleMsg(ctx, &m.config.WebhooksConfig.BeforeSendSingleMsg, data); err != nil {
return err
}
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 servererrs.ErrNotPeersFriend.Wrap()
}
}
// 单聊发送权限校验已迁移至 modifyMessageByUserMessageReceiveOpt
return nil
case constant.ReadGroupChatType:
groupInfo, err := m.GroupLocalCache.GetGroupInfo(ctx, data.MsgData.GroupID)
if err != nil {
log.ZError(ctx, "messageVerification group: GetGroupInfo failed", err,
"groupID", data.MsgData.GroupID, "sendID", data.MsgData.SendID,
"contentType", data.MsgData.ContentType, "clientMsgID", data.MsgData.ClientMsgID)
return err
}
if groupInfo.Status == constant.GroupStatusDismissed &&
@ -126,6 +80,9 @@ func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgRe
}
memberIDs, err := m.GroupLocalCache.GetGroupMemberIDMap(ctx, data.MsgData.GroupID)
if err != nil {
log.ZError(ctx, "messageVerification group: GetGroupMemberIDMap failed", err,
"groupID", data.MsgData.GroupID, "sendID", data.MsgData.SendID,
"contentType", data.MsgData.ContentType, "clientMsgID", data.MsgData.ClientMsgID)
return err
}
if _, ok := memberIDs[data.MsgData.SendID]; !ok {
@ -137,6 +94,9 @@ func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgRe
if errs.ErrRecordNotFound.Is(err) {
return servererrs.ErrNotInGroupYet.WrapMsg(err.Error())
}
log.ZError(ctx, "messageVerification group: GetGroupMember failed", err,
"groupID", data.MsgData.GroupID, "sendID", data.MsgData.SendID,
"contentType", data.MsgData.ContentType, "clientMsgID", data.MsgData.ClientMsgID)
return err
}
if groupMemberInfo.RoleLevel == constant.GroupOwner {
@ -211,21 +171,101 @@ func GetMsgID(sendID string) string {
}
func (m *msgServer) modifyMessageByUserMessageReceiveOpt(ctx context.Context, userID, conversationID string, sessionType int, pb *msg.SendMsgReq) (bool, error) {
// 第一优先级:接收方全局接收设置
// NotReceiveMessage 直接丢弃,无需执行后续任何权限或偏好查询
opt, err := m.UserLocalCache.GetUserGlobalMsgRecvOpt(ctx, userID)
if err != nil {
return false, err
}
switch opt {
case constant.ReceiveMessage:
case constant.NotReceiveMessage:
if opt == constant.NotReceiveMessage {
return false, nil
case constant.ReceiveNotNotifyMessage:
}
if opt == constant.ReceiveNotNotifyMessage {
if pb.MsgData.Options == nil {
pb.MsgData.Options = make(map[string]bool, 10)
}
datautil.SetSwitchFromOptions(pb.MsgData.Options, constant.IsOfflinePush, false)
return true, nil
// 全局静音:仅关闭离线推送,仍需继续执行发送权限校验 + 会话级偏好校验
}
// 第二优先级:单聊发送权限校验(从 messageVerification 迁移)
// 仅对非通知类消息生效(调用方已通过 !isNotification 做过前置过滤)
if sessionType == constant.SingleChatType {
// 管理员跳过发送权限拦截,直接进入接收偏好校验
if !datautil.Contain(pb.MsgData.SendID, m.config.Share.IMAdminUserID...) {
// 黑名单拦截
black, err := m.FriendLocalCache.IsBlack(ctx, pb.MsgData.SendID, pb.MsgData.RecvID)
if err != nil {
log.ZError(ctx, "modifyMessageByUserMessageReceiveOpt: IsBlack failed", err,
"sendID", pb.MsgData.SendID, "recvID", pb.MsgData.RecvID,
"contentType", pb.MsgData.ContentType, "clientMsgID", pb.MsgData.ClientMsgID)
return false, err
}
if black {
return false, servererrs.ErrBlockedByPeer.Wrap()
}
// 接收方消息接收权限MsgReceiveSetting
// 0=所有人可发送1=仅好友可发送2=所有人不可发送
recvUserInfo, err := m.UserLocalCache.GetUserInfo(ctx, pb.MsgData.RecvID)
if err != nil {
log.ZError(ctx, "modifyMessageByUserMessageReceiveOpt: GetUserInfo(recv) failed", err,
"sendID", pb.MsgData.SendID, "recvID", pb.MsgData.RecvID,
"contentType", pb.MsgData.ContentType, "clientMsgID", pb.MsgData.ClientMsgID)
return false, err
}
// skipFriendVerify: MsgReceiveSetting=1 已确认好友关系,无需再做 FriendVerify 重复查询
skipFriendVerify := false
switch recvUserInfo.MsgReceiveSetting {
case 2: // MsgReceiveSettingNobody
return false, servererrs.ErrMsgReceiveNotAllowed.Wrap()
case 1: // MsgReceiveSettingFriends
isFriend, err := m.FriendLocalCache.IsFriend(ctx, pb.MsgData.RecvID, pb.MsgData.SendID)
if err != nil {
log.ZError(ctx, "modifyMessageByUserMessageReceiveOpt: IsFriend failed (MsgReceiveSetting)", err,
"sendID", pb.MsgData.SendID, "recvID", pb.MsgData.RecvID,
"contentType", pb.MsgData.ContentType, "clientMsgID", pb.MsgData.ClientMsgID)
return false, err
}
if !isFriend {
return false, servererrs.ErrMsgReceiveNotAllowed.Wrap()
}
// 已确认好友关系,触发 webhook 后跳过 FriendVerify直接进入接收偏好校验
if err := m.webhookBeforeSendSingleMsg(ctx, &m.config.WebhooksConfig.BeforeSendSingleMsg, pb); err != nil {
log.ZError(ctx, "modifyMessageByUserMessageReceiveOpt: webhookBeforeSendSingleMsg failed (friends-only)", err,
"sendID", pb.MsgData.SendID, "recvID", pb.MsgData.RecvID,
"contentType", pb.MsgData.ContentType, "clientMsgID", pb.MsgData.ClientMsgID)
return false, err
}
skipFriendVerify = true
}
if !skipFriendVerify {
// MsgReceiveSetting==0所有人可发触发 webhook再按全局 FriendVerify 兜底
if err := m.webhookBeforeSendSingleMsg(ctx, &m.config.WebhooksConfig.BeforeSendSingleMsg, pb); err != nil {
log.ZError(ctx, "modifyMessageByUserMessageReceiveOpt: webhookBeforeSendSingleMsg failed", err,
"sendID", pb.MsgData.SendID, "recvID", pb.MsgData.RecvID,
"contentType", pb.MsgData.ContentType, "clientMsgID", pb.MsgData.ClientMsgID)
return false, err
}
if m.config.RpcConfig.FriendVerify {
friend, err := m.FriendLocalCache.IsFriend(ctx, pb.MsgData.SendID, pb.MsgData.RecvID)
if err != nil {
log.ZError(ctx, "modifyMessageByUserMessageReceiveOpt: IsFriend failed (FriendVerify)", err,
"sendID", pb.MsgData.SendID, "recvID", pb.MsgData.RecvID,
"contentType", pb.MsgData.ContentType, "clientMsgID", pb.MsgData.ClientMsgID)
return false, err
}
if !friend {
return false, servererrs.ErrNotPeersFriend.Wrap()
}
}
}
}
}
// 第三优先级:会话级接收偏好
singleOpt, err := m.ConversationLocalCache.GetSingleConversationRecvMsgOpt(ctx, userID, conversationID)
if errs.ErrRecordNotFound.Is(err) {
return true, nil

@ -48,6 +48,7 @@ import (
"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"
)
@ -137,10 +138,14 @@ func (s *userServer) GetDesignateUsers(ctx context.Context, req *pbuser.GetDesig
resp = &pbuser.GetDesignateUsersResp{}
users, err := s.db.Find(ctx, req.UserIDs)
if err != nil {
log.ZError(ctx, "GetDesignateUsers: db.Find failed", err,
"opUserID", mcontext.GetOpUserID(ctx), "reqUserCount", len(req.UserIDs))
return nil, err
}
if blocked, err := s.globalBlackDB.FindBlocked(ctx, req.UserIDs); err != nil {
log.ZError(ctx, "GetDesignateUsers: globalBlackDB.FindBlocked failed", err,
"opUserID", mcontext.GetOpUserID(ctx), "reqUserCount", len(req.UserIDs))
return nil, err
} else if len(blocked) > 0 {
bannedIDs := make([]string, 0, len(blocked))
@ -153,6 +158,8 @@ func (s *userServer) GetDesignateUsers(ctx context.Context, req *pbuser.GetDesig
pbUsers := convert.UsersDB2Pb(users)
viewerID := mcontext.GetOpUserID(ctx)
if err := s.applyPhoneVisibility(ctx, viewerID, pbUsers, users); err != nil {
log.ZError(ctx, "GetDesignateUsers: applyPhoneVisibility failed", err,
"opUserID", viewerID, "userCount", len(users))
return nil, err
}
resp.UsersInfo = pbUsers
@ -182,6 +189,8 @@ func (s *userServer) applyPhoneVisibility(ctx context.Context, viewerID string,
}
isFriend, err := s.relationClient.IsFriend(ctx, viewerID, db.UserID)
if err != nil {
log.ZError(ctx, "applyPhoneVisibility: IsFriend failed", err,
"viewerID", viewerID, "targetUserID", db.UserID)
return err
}
if !isFriend {
@ -288,9 +297,13 @@ func (s *userServer) SetPhoneVisibility(ctx context.Context, req *pbuser.SetPhon
return nil, errs.ErrArgs.WrapMsg("phoneVisibility must be 0, 1 or 2")
}
if err := authverify.CheckAccessV3(ctx, req.UserID, s.config.Share.IMAdminUserID); err != nil {
log.ZWarn(ctx, "SetPhoneVisibility: access denied", err,
"opUserID", mcontext.GetOpUserID(ctx), "targetUserID", req.UserID)
return nil, err
}
if _, err := s.db.FindWithError(ctx, []string{req.UserID}); err != nil {
log.ZError(ctx, "SetPhoneVisibility: user not found or db error", err,
"opUserID", mcontext.GetOpUserID(ctx), "targetUserID", req.UserID)
return nil, err
}
m := map[string]any{
@ -300,6 +313,9 @@ func (s *userServer) SetPhoneVisibility(ctx context.Context, req *pbuser.SetPhon
m["phone"] = req.Phone
}
if err := s.db.UpdateByMap(ctx, req.UserID, m); err != nil {
log.ZError(ctx, "SetPhoneVisibility: UpdateByMap failed", err,
"opUserID", mcontext.GetOpUserID(ctx), "targetUserID", req.UserID,
"phoneVisibility", req.PhoneVisibility, "hasPhoneUpdate", req.Phone != "")
return nil, err
}
s.friendNotificationSender.UserInfoUpdatedNotification(ctx, req.UserID)
@ -316,14 +332,21 @@ func (s *userServer) SetCallAcceptSetting(ctx context.Context, req *pbuser.SetCa
return nil, errs.ErrArgs.WrapMsg("callAcceptSetting must be 0, 1 or 2")
}
if err := authverify.CheckAccessV3(ctx, req.UserID, s.config.Share.IMAdminUserID); err != nil {
log.ZWarn(ctx, "SetCallAcceptSetting: access denied", err,
"opUserID", mcontext.GetOpUserID(ctx), "targetUserID", req.UserID)
return nil, err
}
if _, err := s.db.FindWithError(ctx, []string{req.UserID}); err != nil {
log.ZError(ctx, "SetCallAcceptSetting: user not found or db error", err,
"opUserID", mcontext.GetOpUserID(ctx), "targetUserID", req.UserID)
return nil, err
}
if err := s.db.UpdateByMap(ctx, req.UserID, map[string]any{
"call_accept_setting": req.CallAcceptSetting,
}); err != nil {
log.ZError(ctx, "SetCallAcceptSetting: UpdateByMap failed", err,
"opUserID", mcontext.GetOpUserID(ctx), "targetUserID", req.UserID,
"callAcceptSetting", req.CallAcceptSetting)
return nil, err
}
s.friendNotificationSender.UserInfoUpdatedNotification(ctx, req.UserID)
@ -340,14 +363,21 @@ func (s *userServer) SetMsgReceiveSetting(ctx context.Context, req *pbuser.SetMs
return nil, errs.ErrArgs.WrapMsg("msgReceiveSetting must be 0, 1 or 2")
}
if err := authverify.CheckAccessV3(ctx, req.UserID, s.config.Share.IMAdminUserID); err != nil {
log.ZWarn(ctx, "SetMsgReceiveSetting: access denied", err,
"opUserID", mcontext.GetOpUserID(ctx), "targetUserID", req.UserID)
return nil, err
}
if _, err := s.db.FindWithError(ctx, []string{req.UserID}); err != nil {
log.ZError(ctx, "SetMsgReceiveSetting: user not found or db error", err,
"opUserID", mcontext.GetOpUserID(ctx), "targetUserID", req.UserID)
return nil, err
}
if err := s.db.UpdateByMap(ctx, req.UserID, map[string]any{
"msg_receive_setting": req.MsgReceiveSetting,
}); err != nil {
log.ZError(ctx, "SetMsgReceiveSetting: UpdateByMap failed", err,
"opUserID", mcontext.GetOpUserID(ctx), "targetUserID", req.UserID,
"msgReceiveSetting", req.MsgReceiveSetting)
return nil, err
}
s.friendNotificationSender.UserInfoUpdatedNotification(ctx, req.UserID)

Loading…
Cancel
Save