Skip to content

fix Client failed to withdraw the message #3257

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 86 additions & 17 deletions internal/rpc/msg/revoke.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ package msg
import (
"context"
"encoding/json"
"strconv"
"strings"
"time"

"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
Expand Down Expand Up @@ -49,38 +51,94 @@ func (m *msgServer) RevokeMsg(ctx context.Context, req *msg.RevokeMsgReq) (*msg.
if err != nil {
return nil, err
}

// Get message and add fault tolerance handling
_, _, msgs, err := m.MsgDatabase.GetMsgBySeqs(ctx, req.UserID, req.ConversationID, []int64{req.Seq})
if err != nil {
return nil, err
// Log the error but continue execution
log.ZWarn(ctx, "GetMsgBySeqs error when revoking message", err,
"userID", req.UserID, "conversationID", req.ConversationID, "seq", req.Seq)
} else if len(msgs) == 0 || msgs[0] == nil {
// Check if seq is within valid range for the current conversation
maxSeq, err := m.MsgDatabase.GetMaxSeq(ctx, req.ConversationID)
if err != nil {
log.ZWarn(ctx, "GetMaxSeq error when revoking message", err,
"conversationID", req.ConversationID)
} else if req.Seq > maxSeq {
return nil, errs.ErrArgs.WrapMsg("seq exceeds maxSeq")
}

// Log warning but continue execution
log.ZWarn(ctx, "Message not found when revoking, but will proceed", nil,
"userID", req.UserID, "conversationID", req.ConversationID, "seq", req.Seq)
}

// If message doesn't exist, create a minimal substitute for revocation notification
var msgToRevoke *sdkws.MsgData
if len(msgs) == 0 || msgs[0] == nil {
return nil, errs.ErrRecordNotFound.WrapMsg("msg not found")
}
if msgs[0].ContentType == constant.MsgRevokeNotification {
return nil, servererrs.ErrMsgAlreadyRevoke.WrapMsg("msg already revoke")
// Create a minimal message object with necessary fields
msgToRevoke = &sdkws.MsgData{
SendID: req.UserID, // Use revoker as sender
Seq: req.Seq,
SessionType: getSessionTypeFromConversationID(req.ConversationID), // Helper function to get session type
ClientMsgID: "missing_" + strconv.FormatInt(req.Seq, 10), // Generate a temporary ID
SendTime: time.Now().UnixMilli() - 1000, // Set to a slightly earlier time
}

// Set GroupID or RecvID based on session type
if msgToRevoke.SessionType == constant.ReadGroupChatType {
// Extract group ID from conversation ID
if strings.HasPrefix(req.ConversationID, "sg_") {
msgToRevoke.GroupID = req.ConversationID[3:] // Remove "sg_" prefix
}
} else {
// For single chat, parse receiver ID from conversation ID
if strings.HasPrefix(req.ConversationID, "si_") || strings.HasPrefix(req.ConversationID, "sp_") {
parts := strings.Split(req.ConversationID[3:], "_")
if len(parts) == 2 {
// Conversation ID format is typically: si_senderID_receiverID
// If current user is the sender, then receiver is the other party
if parts[0] == req.UserID {
msgToRevoke.RecvID = parts[1]
} else {
msgToRevoke.RecvID = parts[0]
}
} else {
// Set empty if parsing fails
msgToRevoke.RecvID = ""
}
} else {
msgToRevoke.RecvID = ""
}
}
} else {
msgToRevoke = msgs[0]
if msgToRevoke.ContentType == constant.MsgRevokeNotification {
return nil, servererrs.ErrMsgAlreadyRevoke.WrapMsg("msg already revoke")
}
}

data, _ := json.Marshal(msgs[0])
data, _ := json.Marshal(msgToRevoke)
log.ZDebug(ctx, "GetMsgBySeqs", "conversationID", req.ConversationID, "seq", req.Seq, "msg", string(data))
var role int32
if !authverify.IsAppManagerUid(ctx, m.config.Share.IMAdminUserID) {
sessionType := msgs[0].SessionType
sessionType := msgToRevoke.SessionType
switch sessionType {
case constant.SingleChatType:
if err := authverify.CheckAccessV3(ctx, msgs[0].SendID, m.config.Share.IMAdminUserID); err != nil {
if err := authverify.CheckAccessV3(ctx, msgToRevoke.SendID, m.config.Share.IMAdminUserID); err != nil {
return nil, err
}
role = user.AppMangerLevel
case constant.ReadGroupChatType:
members, err := m.GroupLocalCache.GetGroupMemberInfoMap(ctx, msgs[0].GroupID, datautil.Distinct([]string{req.UserID, msgs[0].SendID}))
members, err := m.GroupLocalCache.GetGroupMemberInfoMap(ctx, msgToRevoke.GroupID, datautil.Distinct([]string{req.UserID, msgToRevoke.SendID}))
if err != nil {
return nil, err
}
if req.UserID != msgs[0].SendID {
if req.UserID != msgToRevoke.SendID {
switch members[req.UserID].RoleLevel {
case constant.GroupOwner:
case constant.GroupAdmin:
if sendMember, ok := members[msgs[0].SendID]; ok {
if sendMember, ok := members[msgToRevoke.SendID]; ok {
if sendMember.RoleLevel != constant.GroupOrdinaryUsers {
return nil, errs.ErrNoPermission.WrapMsg("no permission")
}
Expand Down Expand Up @@ -114,20 +172,31 @@ func (m *msgServer) RevokeMsg(ctx context.Context, req *msg.RevokeMsgReq) (*msg.
}
tips := sdkws.RevokeMsgTips{
RevokerUserID: revokerUserID,
ClientMsgID: msgs[0].ClientMsgID,
ClientMsgID: msgToRevoke.ClientMsgID,
RevokeTime: now,
Seq: req.Seq,
SesstionType: msgs[0].SessionType,
SesstionType: msgToRevoke.SessionType,
ConversationID: req.ConversationID,
IsAdminRevoke: flag,
}
var recvID string
if msgs[0].SessionType == constant.ReadGroupChatType {
recvID = msgs[0].GroupID
if msgToRevoke.SessionType == constant.ReadGroupChatType {
recvID = msgToRevoke.GroupID
} else {
recvID = msgs[0].RecvID
recvID = msgToRevoke.RecvID
}
m.notificationSender.NotificationWithSessionType(ctx, req.UserID, recvID, constant.MsgRevokeNotification, msgs[0].SessionType, &tips)
m.notificationSender.NotificationWithSessionType(ctx, req.UserID, recvID, constant.MsgRevokeNotification, msgToRevoke.SessionType, &tips)
m.webhookAfterRevokeMsg(ctx, &m.config.WebhooksConfig.AfterRevokeMsg, req)
return &msg.RevokeMsgResp{}, nil
}

func getSessionTypeFromConversationID(conversationID string) int32 {
// Conversation ID format is typically: "single chat prefix{userID}" or "group chat prefix{groupID}"
if strings.HasPrefix(conversationID, "sp_") || strings.HasPrefix(conversationID, "si_") {
return constant.SingleChatType
} else if strings.HasPrefix(conversationID, "sg_") {
return constant.ReadGroupChatType
}
// Default to single chat type
return constant.SingleChatType
}