MM-62745: [Shared Channels] Fix duplicate mentioning - local user with the same username as someone on the remote server - Part2 (#32101) (#33414)
Automatic Merge
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
b6e80b9f59
Коммит
7f4fbd803a
@@ -380,3 +380,8 @@ func (scs *Service) OnReceiveSyncMessageForTesting(msg model.RemoteClusterMsg, r
|
||||
func (scs *Service) HandleChannelNotSharedErrorForTesting(msg *model.SyncMsg, rc *model.RemoteCluster) {
|
||||
scs.handleChannelNotSharedError(msg, rc)
|
||||
}
|
||||
|
||||
// TransformMentionsOnReceiveForTesting allows testing the full mention transformation flow
|
||||
func (scs *Service) TransformMentionsOnReceiveForTesting(ctx request.CTX, post *model.Post, targetChannel *model.Channel, rc *model.RemoteCluster, mentionTransforms map[string]string) {
|
||||
scs.transformMentionsOnReceive(ctx, post, targetChannel, rc, mentionTransforms)
|
||||
}
|
||||
|
||||
@@ -197,7 +197,7 @@ func (scs *Service) processSyncMessage(c request.CTX, syncMsg *model.SyncMsg, rc
|
||||
}
|
||||
|
||||
// add/update post
|
||||
rpost, err := scs.upsertSyncPost(post, targetChannel, rc)
|
||||
rpost, err := scs.upsertSyncPost(post, targetChannel, rc, syncMsg.MentionTransforms)
|
||||
if err != nil {
|
||||
syncResp.PostErrors = append(syncResp.PostErrors, post.Id)
|
||||
scs.server.Log().Log(mlog.LvlSharedChannelServiceError, "Error upserting sync post",
|
||||
@@ -454,7 +454,7 @@ func (scs *Service) updateSyncUser(rctx request.CTX, patch *model.UserPatch, use
|
||||
return nil, fmt.Errorf("error updating sync user %s: %w", user.Id, err)
|
||||
}
|
||||
|
||||
func (scs *Service) upsertSyncPost(post *model.Post, targetChannel *model.Channel, rc *model.RemoteCluster) (*model.Post, error) {
|
||||
func (scs *Service) upsertSyncPost(post *model.Post, targetChannel *model.Channel, rc *model.RemoteCluster, mentionTransforms map[string]string) (*model.Post, error) {
|
||||
var appErr *model.AppError
|
||||
|
||||
post.RemoteId = model.NewPointer(rc.RemoteId)
|
||||
@@ -483,11 +483,14 @@ func (scs *Service) upsertSyncPost(post *model.Post, targetChannel *model.Channe
|
||||
return nil, fmt.Errorf("post sync failed: %w", ErrRemoteIDMismatch)
|
||||
}
|
||||
|
||||
scs.transformMentionsOnReceive(rctx, post, targetChannel, rc, mentionTransforms)
|
||||
|
||||
rpost, appErr = scs.app.CreatePost(rctx, post, targetChannel, model.CreatePostFlags{TriggerWebhooks: true, SetOnline: true})
|
||||
if appErr == nil {
|
||||
scs.server.Log().Log(mlog.LvlSharedChannelServiceDebug, "Created sync post",
|
||||
mlog.String("post_id", post.Id),
|
||||
mlog.String("channel_id", post.ChannelId))
|
||||
mlog.String("channel_id", post.ChannelId),
|
||||
)
|
||||
}
|
||||
} else if post.DeleteAt > 0 {
|
||||
// delete post
|
||||
@@ -499,6 +502,7 @@ func (scs *Service) upsertSyncPost(post *model.Post, targetChannel *model.Channe
|
||||
)
|
||||
}
|
||||
} else if post.EditAt > rpost.EditAt || post.Message != rpost.Message || post.UpdateAt > rpost.UpdateAt || post.Metadata != nil {
|
||||
scs.transformMentionsOnReceive(rctx, post, targetChannel, rc, mentionTransforms)
|
||||
var priority *model.PostPriority
|
||||
var acknowledgements []*model.PostAcknowledgement
|
||||
|
||||
@@ -741,3 +745,44 @@ func (scs *Service) upsertSyncAcknowledgement(acknowledgement *model.PostAcknowl
|
||||
}
|
||||
return savedAcknowledgement, retErr
|
||||
}
|
||||
|
||||
// transformMentionsOnReceive transforms mentions in received posts using explicit mentionTransforms.
|
||||
func (scs *Service) transformMentionsOnReceive(rctx request.CTX, post *model.Post, targetChannel *model.Channel, rc *model.RemoteCluster, mentionTransforms map[string]string) {
|
||||
if post.Message == "" || len(mentionTransforms) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Process mentions directly using mentionTransforms - no need to re-parse with regex
|
||||
for mention, userID := range mentionTransforms {
|
||||
oldMention := "@" + mention
|
||||
var newMention string
|
||||
|
||||
// Get the user to determine transformation type
|
||||
if user, err := scs.server.GetStore().User().Get(context.Background(), userID); err == nil && user != nil {
|
||||
// User exists in receiver's database
|
||||
if strings.Contains(mention, ":") {
|
||||
// Colon mention (e.g., "@admin:remote1") - always use the user's actual username
|
||||
newMention = "@" + user.Username
|
||||
} else {
|
||||
// Simple mention (e.g., "@admin")
|
||||
if user.GetRemoteID() == "" {
|
||||
// This is a local user, keep as-is
|
||||
newMention = "@" + mention
|
||||
} else {
|
||||
// This is a remote user that was synced, use their synced username
|
||||
newMention = "@" + user.Username
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// User doesn't exist in receiver's database
|
||||
if strings.Contains(mention, ":") {
|
||||
// Colon mention for unknown user - keep as-is
|
||||
newMention = oldMention
|
||||
} else {
|
||||
// Simple mention for unknown user - add cluster suffix to indicate it's from remote
|
||||
newMention = "@" + mention + ":" + rc.Name
|
||||
}
|
||||
}
|
||||
post.Message = strings.ReplaceAll(post.Message, oldMention, newMention)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,13 +32,14 @@ type syncData struct {
|
||||
rc *model.RemoteCluster
|
||||
scr *model.SharedChannelRemote
|
||||
|
||||
users map[string]*model.User
|
||||
profileImages map[string]*model.User
|
||||
posts []*model.Post
|
||||
reactions []*model.Reaction
|
||||
acknowledgements []*model.PostAcknowledgement
|
||||
statuses []*model.Status
|
||||
attachments []attachment
|
||||
users map[string]*model.User
|
||||
profileImages map[string]*model.User
|
||||
posts []*model.Post
|
||||
reactions []*model.Reaction
|
||||
acknowledgements []*model.PostAcknowledgement
|
||||
statuses []*model.Status
|
||||
attachments []attachment
|
||||
mentionTransforms map[string]string
|
||||
|
||||
resultRepeat bool
|
||||
resultNextCursor model.GetPostsSinceForSyncCursor
|
||||
@@ -47,11 +48,12 @@ type syncData struct {
|
||||
|
||||
func newSyncData(task syncTask, rc *model.RemoteCluster, scr *model.SharedChannelRemote) *syncData {
|
||||
return &syncData{
|
||||
task: task,
|
||||
rc: rc,
|
||||
scr: scr,
|
||||
users: make(map[string]*model.User),
|
||||
profileImages: make(map[string]*model.User),
|
||||
task: task,
|
||||
rc: rc,
|
||||
scr: scr,
|
||||
users: make(map[string]*model.User),
|
||||
profileImages: make(map[string]*model.User),
|
||||
mentionTransforms: make(map[string]string),
|
||||
resultNextCursor: model.GetPostsSinceForSyncCursor{
|
||||
LastPostUpdateAt: scr.LastPostUpdateAt, LastPostUpdateID: scr.LastPostUpdateID,
|
||||
LastPostCreateAt: scr.LastPostCreateAt, LastPostCreateID: scr.LastPostCreateID,
|
||||
@@ -444,9 +446,6 @@ func (scs *Service) fetchPostUsersForSync(sd *syncData) error {
|
||||
}
|
||||
|
||||
for _, post := range sd.posts {
|
||||
// add author
|
||||
userIDs[post.UserId] = p2mm{}
|
||||
|
||||
// get mentions and users for each mention
|
||||
mentionMap := scs.app.MentionsToTeamMembers(request.EmptyContext(scs.server.Log()), post.Message, sc.TeamId)
|
||||
|
||||
@@ -458,10 +457,19 @@ func (scs *Service) fetchPostUsersForSync(sd *syncData) error {
|
||||
}
|
||||
|
||||
// Skip remote users unless mention contains a colon (@username:remote)
|
||||
if user.RemoteId != nil && !strings.Contains(mention, ":") {
|
||||
if user.IsRemote() && !strings.Contains(mention, ":") {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// add author with post and mentionMap so transformations can be applied
|
||||
userIDs[post.UserId] = p2mm{
|
||||
post: post,
|
||||
mentionMap: mentionMap,
|
||||
}
|
||||
|
||||
// Add all mentioned users
|
||||
for _, userID := range mentionMap {
|
||||
userIDs[userID] = p2mm{
|
||||
post: post,
|
||||
mentionMap: mentionMap,
|
||||
@@ -470,7 +478,6 @@ func (scs *Service) fetchPostUsersForSync(sd *syncData) error {
|
||||
}
|
||||
|
||||
merr := merror.New()
|
||||
|
||||
for userID, v := range userIDs {
|
||||
user, err := scs.server.GetStore().User().Get(context.Background(), userID)
|
||||
if err != nil {
|
||||
@@ -480,7 +487,7 @@ func (scs *Service) fetchPostUsersForSync(sd *syncData) error {
|
||||
|
||||
sync, syncImage, err2 := scs.shouldUserSync(user, sd.task.channelID, sd.rc)
|
||||
if err2 != nil {
|
||||
merr.Append(fmt.Errorf("could not check should sync user %s: %w", userID, err))
|
||||
merr.Append(fmt.Errorf("could not check should sync user %s: %w", userID, err2))
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -492,9 +499,15 @@ func (scs *Service) fetchPostUsersForSync(sd *syncData) error {
|
||||
sd.profileImages[user.Id] = user
|
||||
}
|
||||
|
||||
// Transform @username:remote to @username when sending to a user's home cluster
|
||||
if v.post != nil && user.RemoteId != nil && *user.RemoteId == sd.rc.RemoteId {
|
||||
fixMention(v.post, v.mentionMap, user)
|
||||
// Collect mention transforms for all mentioned users
|
||||
if v.mentionMap != nil {
|
||||
for mention, mentionUserID := range v.mentionMap {
|
||||
if mentionUserID == userID {
|
||||
// Always add the mention transform - let receiver decide how to display
|
||||
// The sender should NOT modify the message, only provide the mapping
|
||||
sd.mentionTransforms[mention] = userID
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return merr.ErrorOrNil()
|
||||
@@ -690,6 +703,7 @@ func (scs *Service) sendPostSyncData(sd *syncData) error {
|
||||
|
||||
msg := model.NewSyncMsg(sd.task.channelID)
|
||||
msg.Posts = sd.posts
|
||||
msg.MentionTransforms = sd.mentionTransforms
|
||||
|
||||
return scs.sendSyncMsgToRemote(msg, sd.rc, func(syncResp model.SyncResponse, errResp error) {
|
||||
if len(syncResp.PostErrors) != 0 {
|
||||
|
||||
Ссылка в новой задаче
Block a user