Add support for reactions from Matrix

This commit is contained in:
Tulir Asokan 2023-07-15 01:06:49 +03:00
parent 376f908a03
commit b5257f53d3
3 changed files with 85 additions and 5 deletions

View file

@ -4,7 +4,7 @@
* [x] Plain text
* [ ] Media/files
* [x] Replies (RCS)
* [ ] Reactions (RCS)
* [x] Reactions (RCS)
* [ ] Typing notifications (RCS)
* [ ] Read receipts (RCS)
* Google Messages → Matrix
@ -12,9 +12,9 @@
* [x] Plain text
* [x] Media/files
* [x] Replies (RCS)
* [ ] Reactions
* [ ] Typing notifications
* [ ] Read receipts
* [x] Reactions (RCS)
* [ ] Typing notifications (RCS)
* [ ] Read receipts (RCS)
* Misc
* [x] Automatic portal creation
* [x] After login

View file

@ -39,6 +39,7 @@ var (
errUnexpectedParsedContentType = errors.New("unexpected parsed content type")
errUnknownMsgType = errors.New("unknown msgtype")
errMediaUnsupportedType = errors.New("unsupported media type")
errTargetNotFound = errors.New("target event not found")
errMessageTakingLong = errors.New("bridging the message is taking longer than usual")
)
@ -73,6 +74,8 @@ func errorToStatusReason(err error) (reason event.MessageStatusReason, status ev
return event.MessageStatusUnsupported, event.MessageStatusFail, true, true, err.Error()
case errors.Is(err, context.DeadlineExceeded):
return event.MessageStatusTooOld, event.MessageStatusRetriable, false, true, "handling the message took too long and was cancelled"
case errors.Is(err, errTargetNotFound):
return event.MessageStatusGenericError, event.MessageStatusFail, true, false, ""
case errors.As(err, &ose):
return event.MessageStatusNetworkError, event.MessageStatusFail, true, true, ose.HumanError()
default:
@ -187,6 +190,9 @@ func (portal *Portal) sendMessageMetrics(evt *event.Event, err error, part strin
portal.log.Debugfln("Handled Matrix %s %s", msgType, evtDescription)
portal.sendDeliveryReceipt(evt.ID)
portal.bridge.SendMessageSuccessCheckpoint(evt, status.MsgStepRemote, ms.getRetryNum())
if msgType != "message" {
portal.sendStatusEvent(origEvtID, evt.ID, nil)
}
if prevNotice := ms.popNoticeID(); prevNotice != "" {
_, _ = portal.MainIntent().RedactEvent(portal.MXID, prevNotice, mautrix.ReqRedact{
Reason: "error resolved",

View file

@ -1174,7 +1174,10 @@ func (portal *Portal) uploadMedia(intent *appservice.IntentAPI, data []byte, con
func (portal *Portal) HandleMatrixMessage(sender *User, evt *event.Event, timings messageTimings) {
ms := metricSender{portal: portal, timings: &timings}
log := portal.zlog.With().Str("event_id", evt.ID.String()).Logger()
log := portal.zlog.With().
Str("event_id", evt.ID.String()).
Str("action", "handle matrix message").
Logger()
ctx := log.WithContext(context.TODO())
log.Debug().Dur("age", timings.totalReceive).Msg("Handling Matrix message")
@ -1225,7 +1228,78 @@ func (portal *Portal) HandleMatrixMessage(sender *User, evt *event.Event, timing
}
func (portal *Portal) HandleMatrixReaction(sender *User, evt *event.Event) {
err := portal.handleMatrixReaction(sender, evt)
go portal.sendMessageMetrics(evt, err, "Error sending", nil)
}
func (portal *Portal) handleMatrixReaction(sender *User, evt *event.Event) error {
content, ok := evt.Content.Parsed.(*event.ReactionEventContent)
if !ok {
return fmt.Errorf("unexpected parsed content type %T", evt.Content.Parsed)
}
log := portal.zlog.With().
Str("event_id", evt.ID.String()).
Str("target_event_id", content.RelatesTo.EventID.String()).
Str("action", "handle matrix reaction").
Logger()
ctx := log.WithContext(context.Background())
log.Debug().Msg("Handling Matrix reaction")
msg, err := portal.bridge.DB.Message.GetByMXID(ctx, content.RelatesTo.EventID)
if err != nil {
log.Err(err).Msg("Failed to get reaction target event")
return fmt.Errorf("failed to get event from database")
} else if msg == nil {
return errTargetNotFound
}
existingReaction, err := portal.bridge.DB.Reaction.GetByID(ctx, portal.Key, msg.ID, portal.SelfUserID)
if err != nil {
log.Err(err).Msg("Failed to get existing reaction")
return fmt.Errorf("failed to get existing reaction from database")
}
emoji := variationselector.Remove(content.RelatesTo.Key)
action := binary.Reaction_ADD
if existingReaction != nil {
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,
})
if err != nil {
return fmt.Errorf("failed to send reaction: %w", err)
} else if !resp.Success {
return fmt.Errorf("got non-success response")
}
if existingReaction == nil {
existingReaction = portal.bridge.DB.Reaction.New()
existingReaction.Chat = portal.Key
existingReaction.MessageID = msg.ID
existingReaction.Sender = portal.SelfUserID
} else if sender.DoublePuppetIntent != nil {
_, err = sender.DoublePuppetIntent.RedactEvent(portal.MXID, existingReaction.MXID)
if err != nil {
log.Err(err).Msg("Failed to redact old reaction with double puppet after new Matrix reaction")
}
} else {
_, err = portal.MainIntent().RedactEvent(portal.MXID, existingReaction.MXID)
if err != nil {
log.Err(err).Msg("Failed to redact old reaction with main intent after new Matrix reaction")
}
}
existingReaction.Reaction = emoji
existingReaction.MXID = evt.ID
err = existingReaction.Insert(ctx)
if err != nil {
log.Err(err).Msg("Failed to save reaction from Matrix to database")
}
return nil
}
func (portal *Portal) Delete() {