diff --git a/ROADMAP.md b/ROADMAP.md index 31815d1..6ed2604 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -7,6 +7,7 @@ * [x] Reactions (RCS) * [ ] Typing notifications (RCS) * [ ] Read receipts (RCS) + * [x] Message deletions (own device only) * Google Messages → Matrix * [ ] Message content * [x] Plain text @@ -15,6 +16,7 @@ * [x] Reactions (RCS) * [ ] Typing notifications (RCS) * [ ] Read receipts (RCS) + * [x] Message deletions (own device only) * Misc * [x] Automatic portal creation * [x] After login diff --git a/libgm/binary/protoUtil.go b/libgm/binary/protoUtil.go index 2766a13..a0d31d7 100644 --- a/libgm/binary/protoUtil.go +++ b/libgm/binary/protoUtil.go @@ -79,3 +79,10 @@ func UnicodeToEmojiType(emoji string) EmojiType { return EmojiType_CUSTOM } } + +func MakeReactionData(emoji string) *ReactionData { + return &ReactionData{ + Unicode: emoji, + Type: UnicodeToEmojiType(emoji), + } +} diff --git a/portal.go b/portal.go index 1f3b9fa..294e9f7 100644 --- a/portal.go +++ b/portal.go @@ -291,6 +291,8 @@ func (portal *Portal) handleMatrixMessageLoopItem(msg PortalMatrixMessage) { portal.HandleMatrixMessage(msg.user, msg.evt, timings) case event.EventReaction: portal.HandleMatrixReaction(msg.user, msg.evt) + case event.EventRedaction: + portal.HandleMatrixRedaction(msg.user, msg.evt) default: portal.zlog.Warn(). Str("event_type", msg.evt.Type.Type). @@ -365,7 +367,8 @@ func (portal *Portal) handleMessage(source *User, evt *binary.Message) { log := portal.zlog.With(). Str("message_id", evt.MessageID). Str("participant_id", evt.ParticipantID). - Str("action", "handleMessage"). + Str("status", evt.GetMessageStatus().GetStatus().String()). + Str("action", "handle google message"). Logger() ctx := log.WithContext(context.TODO()) switch evt.GetMessageStatus().GetStatus() { @@ -373,7 +376,7 @@ func (portal *Portal) handleMessage(source *User, evt *binary.Message) { log.Debug().Msg("Not handling incoming message that is auto downloading") return case binary.MessageStatusType_MESSAGE_DELETED: - // TODO handle deletion + portal.handleGoogleDeletion(ctx, evt.MessageID) return } if hasInProgressMedia(evt) { @@ -412,6 +415,24 @@ func (portal *Portal) handleMessage(source *User, evt *binary.Message) { log.Debug().Interface("event_ids", eventIDs).Msg("Handled message") } +func (portal *Portal) handleGoogleDeletion(ctx context.Context, messageID string) { + log := zerolog.Ctx(ctx) + msg, err := portal.bridge.DB.Message.GetByID(ctx, portal.Key, messageID) + if err != nil { + log.Err(err).Msg("Failed to get deleted message from database") + } else if msg == nil { + log.Debug().Msg("Didn't find deleted message in database") + } else { + if _, err = portal.MainIntent().RedactEvent(portal.MXID, msg.MXID); err != nil { + log.Err(err).Msg("Faield to redact deleted message") + } + if err = msg.Delete(ctx); err != nil { + log.Err(err).Msg("Failed to delete message from database") + } + log.Debug().Msg("Handled message deletion") + } +} + func (portal *Portal) syncReactions(ctx context.Context, source *User, message *database.Message, reactions []*binary.ReactionResponse) { log := zerolog.Ctx(ctx) existing, err := portal.bridge.DB.Reaction.GetAllByMessage(ctx, portal.Key, message.ID) @@ -1242,7 +1263,7 @@ func (portal *Portal) handleMatrixReaction(sender *User, evt *event.Event) error Str("target_event_id", content.RelatesTo.EventID.String()). Str("action", "handle matrix reaction"). Logger() - ctx := log.WithContext(context.Background()) + ctx := log.WithContext(context.TODO()) log.Debug().Msg("Handling Matrix reaction") msg, err := portal.bridge.DB.Message.GetByMXID(ctx, content.RelatesTo.EventID) @@ -1265,12 +1286,9 @@ func (portal *Portal) handleMatrixReaction(sender *User, evt *event.Event) error action = binary.Reaction_SWITCH } resp, err := sender.Client.Messages.React(&binary.SendReactionPayload{ - MessageID: msg.ID, - ReactionData: &binary.ReactionData{ - Unicode: emoji, - Type: binary.UnicodeToEmojiType(emoji), - }, - Action: action, + MessageID: msg.ID, + ReactionData: binary.MakeReactionData(emoji), + Action: action, }) if err != nil { return fmt.Errorf("failed to send reaction: %w", err) @@ -1302,6 +1320,76 @@ func (portal *Portal) handleMatrixReaction(sender *User, evt *event.Event) error return nil } +func (portal *Portal) HandleMatrixRedaction(sender *User, evt *event.Event) { + err := portal.handleMatrixRedaction(sender, evt) + go portal.sendMessageMetrics(evt, err, "Error sending", nil) +} + +func (portal *Portal) handleMatrixMessageRedaction(ctx context.Context, sender *User, redacts id.EventID) error { + log := zerolog.Ctx(ctx) + msg, err := portal.bridge.DB.Message.GetByMXID(ctx, redacts) + if err != nil { + log.Err(err).Msg("Failed to get redaction target message") + return fmt.Errorf("failed to get event from database") + } else if msg == nil { + return errTargetNotFound + } + resp, err := sender.Client.Messages.Delete(msg.ID) + if err != nil { + return fmt.Errorf("failed to send message removal: %w", err) + } else if !resp.Success { + return fmt.Errorf("got non-success response") + } + err = msg.Delete(ctx) + if err != nil { + log.Err(err).Msg("Failed to delete message from database after Matrix redaction") + } + return nil +} + +func (portal *Portal) handleMatrixReactionRedaction(ctx context.Context, sender *User, redacts id.EventID) error { + log := zerolog.Ctx(ctx) + existingReaction, err := portal.bridge.DB.Reaction.GetByMXID(ctx, redacts) + if err != nil { + log.Err(err).Msg("Failed to get redaction target reaction") + return fmt.Errorf("failed to get event from database") + } else if existingReaction == nil { + return errTargetNotFound + } + + resp, err := sender.Client.Messages.React(&binary.SendReactionPayload{ + MessageID: existingReaction.MessageID, + ReactionData: binary.MakeReactionData(existingReaction.Reaction), + Action: binary.Reaction_REMOVE, + }) + if err != nil { + return fmt.Errorf("failed to send reaction removal: %w", err) + } else if !resp.Success { + return fmt.Errorf("got non-success response") + } + err = existingReaction.Delete(ctx) + if err != nil { + log.Err(err).Msg("Failed to remove reaction from database after Matrix redaction") + } + return nil +} + +func (portal *Portal) handleMatrixRedaction(sender *User, evt *event.Event) error { + log := portal.zlog.With(). + Str("event_id", evt.ID.String()). + Str("target_event_id", evt.Redacts.String()). + Str("action", "handle matrix redaction"). + Logger() + ctx := log.WithContext(context.TODO()) + log.Debug().Msg("Handling Matrix redaction") + + err := portal.handleMatrixMessageRedaction(ctx, sender, evt.Redacts) + if err == errTargetNotFound { + err = portal.handleMatrixReactionRedaction(ctx, sender, evt.Redacts) + } + return err +} + func (portal *Portal) Delete() { err := portal.Portal.Delete(context.TODO()) if err != nil {