spark-server/internal/module/message/logic/recall.go
2026-09-18 14:50:41 +08:00

69 lines
1.7 KiB
Go

package logic
import (
"context"
"errors"
"time"
deliverentity "spark-server/internal/module/deliver/entity"
"spark-server/internal/module/message/entity"
"spark-server/internal/pkg/errs"
)
const cmdRecall uint16 = 9
func (s *Service) RecallMessage(ctx context.Context, userID int64, deviceID uint64, msgID int64) error {
if !s.ready() || userID <= 0 {
return errs.ErrUnauthorized
}
if msgID <= 0 {
return errs.ErrInvalidArgument
}
msg, err := s.repo.GetByID(ctx, msgID)
if err != nil {
return err
}
if msg.SenderID != userID || msg.SenderID == 0 {
return errs.ErrForbidden
}
if _, err := s.chats.GetMember(ctx, msg.ConversationID, userID); err != nil {
if errors.Is(err, errs.ErrNotFound) {
return errs.ErrForbidden
}
return err
}
if msg.Status == entity.StatusRecalled {
return s.enqueueRecall(ctx, msg, userID, deviceID)
}
if msg.Status != entity.StatusNormal {
return errs.ErrForbidden
}
if time.Since(time.UnixMilli(msg.CreatedAtMS)) > recallWindow {
return errs.ErrForbidden
}
if err := s.repo.Recall(ctx, msg.ID); err != nil {
return err
}
msg.Status = entity.StatusRecalled
return s.enqueueRecall(ctx, msg, userID, deviceID)
}
func (s *Service) enqueueRecall(ctx context.Context, msg *entity.Message, userID int64, deviceID uint64) error {
if s.queue == nil {
return nil
}
jobID, err := s.ids.NextID()
if err != nil {
return err
}
members, err := s.chats.ListMemberIDs(ctx, msg.ConversationID)
if err != nil {
return err
}
return s.queue.Push(ctx, &deliverentity.Job{
MsgID: jobID, Cmd: cmdRecall, ConversationID: msg.ConversationID,
FromUserID: userID, FromDeviceID: deviceID, ExcludeDevice: deviceID,
ToUserIDs: members, Payload: controlBytes(msg.ID, msg.ConversationID), Preview: previewOf(msg),
})
}