Compare commits

...

5 Commits

Author SHA1 Message Date
adru
09b7e5679a
Merge a0ccbf18839d567358d2519ceec8d37a06be1932 into 1178808ba7d7798cfc13aada0b47c4a1e130cdce 2025-05-10 17:18:51 +08:00
chao
1178808ba7
feat: optimize server code (#3319)
* 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

* upgrading pkg tools

* fix

* fix

* optimize log output

* feat: support GetLastMessage

* feat: support GetLastMessage

* feat: s3 switch

* feat: s3 switch

* fix: GetUsersOnline

* feat: SendBusinessNotification supported configuration parameters

* feat: SendBusinessNotification supported configuration parameters

* feat: SendBusinessNotification supported configuration parameters

* feat: seq conversion failed without exiting

* fix: DeleteDoc crash

* fix: fill send time

* fix: fill send time

* fix: crash caused by withdrawing messages from users who have left the group

* fix: user msg timestamp

* seq read config

* seq read config

* fix: the source message of the reference is withdrawn, and the referenced message is deleted

* feat: optimize the default notification.yml

* fix: shouldPushOffline

* fix: the sorting is wrong after canceling the administrator in group settings

* feat: Sending messages supports returning fields modified by webhook

* feat: Sending messages supports returning fields modified by webhook

* feat: Sending messages supports returning fields modified by webhook

* fix: oss specifies content-type when uploading

* fix: the version number contains a line break

* fix: the version number contains a line break

* feat: GetConversationsHasReadAndMaxSeq support pinned

* feat: GetConversationsHasReadAndMaxSeq support pinned

* feat: GetConversationsHasReadAndMaxSeq support pinned

* fix: transferring the group owner to a muted member, incremental version error

* feat: unified conversion code

* feat: update gomake
2025-05-09 01:31:30 +00:00
adru
a0ccbf1883 fix Client failed to withdraw the message #3241 2025-03-27 00:42:40 +08:00
adru
d13d41f99f fix Client failed to withdraw the message #3241 2025-03-27 00:39:42 +08:00
adru
3651714950 fix Client failed to withdraw the message 2025-03-27 00:03:40 +08:00
4 changed files with 92 additions and 55 deletions

2
go.mod
View File

@ -34,7 +34,7 @@ require (
github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/kelindar/bitmap v1.5.2
github.com/likexian/gokit v0.25.13
github.com/openimsdk/gomake v0.0.15-alpha.2
github.com/openimsdk/gomake v0.0.15-alpha.5
github.com/redis/go-redis/v9 v9.4.0
github.com/robfig/cron/v3 v3.0.1
github.com/shirou/gopsutil v3.21.11+incompatible

4
go.sum
View File

@ -345,8 +345,8 @@ github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA
github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
github.com/onsi/gomega v1.25.0 h1:Vw7br2PCDYijJHSfBOWhov+8cAnUf8MfMaIOV323l6Y=
github.com/onsi/gomega v1.25.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM=
github.com/openimsdk/gomake v0.0.15-alpha.2 h1:5Q8yl8ezy2yx+q8/ucU/t4kJnDfCzNOrkXcDACCqtyM=
github.com/openimsdk/gomake v0.0.15-alpha.2/go.mod h1:PndCozNc2IsQIciyn9mvEblYWZwJmAI+06z94EY+csI=
github.com/openimsdk/gomake v0.0.15-alpha.5 h1:eEZCEHm+NsmcO3onXZPIUbGFCYPYbsX5beV3ZyOsGhY=
github.com/openimsdk/gomake v0.0.15-alpha.5/go.mod h1:PndCozNc2IsQIciyn9mvEblYWZwJmAI+06z94EY+csI=
github.com/openimsdk/protocol v0.0.73-alpha.6 h1:sna9coWG7HN1zObBPtvG0Ki/vzqHXiB4qKbA5P3w7kc=
github.com/openimsdk/protocol v0.0.73-alpha.6/go.mod h1:WF7EuE55vQvpyUAzDXcqg+B+446xQyEba0X35lTINmw=
github.com/openimsdk/tools v0.0.50-alpha.79 h1:jxYEbrzaze4Z2r4NrKad816buZ690ix0L9MTOOOH3ik=

View File

@ -17,6 +17,8 @@ package msg
import (
"context"
"encoding/json"
"strconv"
"strings"
"time"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
@ -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
}
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")
// 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)
}
data, _ := json.Marshal(msgs[0])
// If message doesn't exist, create a minimal substitute for revocation notification
var msgToRevoke *sdkws.MsgData
if len(msgs) == 0 || msgs[0] == nil {
// 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(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")
}
@ -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
}

View File

@ -81,42 +81,10 @@ func (db *msgTransferDatabase) BatchInsertChat2DB(ctx context.Context, conversat
continue
}
seqs[i] = msg.Seq
var offlinePushModel *model.OfflinePushModel
if msg.OfflinePushInfo != nil {
offlinePushModel = &model.OfflinePushModel{
Title: msg.OfflinePushInfo.Title,
Desc: msg.OfflinePushInfo.Desc,
Ex: msg.OfflinePushInfo.Ex,
IOSPushSound: msg.OfflinePushInfo.IOSPushSound,
IOSBadgeCount: msg.OfflinePushInfo.IOSBadgeCount,
}
}
if msg.Status == constant.MsgStatusSending {
msg.Status = constant.MsgStatusSendSuccess
}
msgs[i] = &model.MsgDataModel{
SendID: msg.SendID,
RecvID: msg.RecvID,
GroupID: msg.GroupID,
ClientMsgID: msg.ClientMsgID,
ServerMsgID: msg.ServerMsgID,
SenderPlatformID: msg.SenderPlatformID,
SenderNickname: msg.SenderNickname,
SenderFaceURL: msg.SenderFaceURL,
SessionType: msg.SessionType,
MsgFrom: msg.MsgFrom,
ContentType: msg.ContentType,
Content: string(msg.Content),
Seq: msg.Seq,
SendTime: msg.SendTime,
CreateTime: msg.CreateTime,
Status: msg.Status,
Options: msg.Options,
OfflinePush: offlinePushModel,
AtUserIDList: msg.AtUserIDList,
AttachedInfo: msg.AttachedInfo,
Ex: msg.Ex,
}
msgs[i] = convert.MsgPb2DB(msg)
}
if err := db.BatchInsertBlock(ctx, conversationID, msgs, updateKeyMsg, msgList[0].Seq); err != nil {
return err