mirror of
https://mau.dev/mautrix/go.git
synced 2026-03-14 22:35:52 +01:00
bridgev2/events: add reaction mass resync event
This commit is contained in:
parent
6042dbec53
commit
3e01676eb5
2 changed files with 125 additions and 0 deletions
|
|
@ -635,6 +635,8 @@ func (ret RemoteEventType) String() string {
|
|||
return "RemoteEventReaction"
|
||||
case RemoteEventReactionRemove:
|
||||
return "RemoteEventReactionRemove"
|
||||
case RemoteEventReactionSync:
|
||||
return "RemoteEventReactionSync"
|
||||
case RemoteEventMessageRemove:
|
||||
return "RemoteEventMessageRemove"
|
||||
case RemoteEventReadReceipt:
|
||||
|
|
@ -664,6 +666,7 @@ const (
|
|||
RemoteEventEdit
|
||||
RemoteEventReaction
|
||||
RemoteEventReactionRemove
|
||||
RemoteEventReactionSync
|
||||
RemoteEventMessageRemove
|
||||
RemoteEventReadReceipt
|
||||
RemoteEventDeliveryReceipt
|
||||
|
|
@ -751,6 +754,23 @@ type RemoteReaction interface {
|
|||
GetReactionEmoji() (string, networkid.EmojiID)
|
||||
}
|
||||
|
||||
type ReactionSyncUser struct {
|
||||
Reactions []*BackfillReaction
|
||||
// Whether the list contains all reactions the user has sent
|
||||
HasAllReactions bool
|
||||
}
|
||||
|
||||
type ReactionSyncData struct {
|
||||
Users map[networkid.UserID]*ReactionSyncUser
|
||||
// Whether the map contains all users who have reacted to the message
|
||||
HasAllUsers bool
|
||||
}
|
||||
|
||||
type RemoteReactionSync interface {
|
||||
RemoteEventWithTargetMessage
|
||||
GetReactions() *ReactionSyncData
|
||||
}
|
||||
|
||||
type RemoteReactionWithExtraContent interface {
|
||||
RemoteReaction
|
||||
GetReactionExtraContent() map[string]any
|
||||
|
|
|
|||
|
|
@ -1235,6 +1235,8 @@ func (portal *Portal) handleRemoteEvent(source *UserLogin, evt RemoteEvent) {
|
|||
portal.handleRemoteReaction(ctx, source, evt.(RemoteReaction))
|
||||
case RemoteEventReactionRemove:
|
||||
portal.handleRemoteReactionRemove(ctx, source, evt.(RemoteReactionRemove))
|
||||
case RemoteEventReactionSync:
|
||||
portal.handleRemoteReactionSync(ctx, source, evt.(RemoteReactionSync))
|
||||
case RemoteEventMessageRemove:
|
||||
portal.handleRemoteMessageRemove(ctx, source, evt.(RemoteMessageRemove))
|
||||
case RemoteEventReadReceipt:
|
||||
|
|
@ -1569,6 +1571,109 @@ func getEventTS(evt RemoteEvent) time.Time {
|
|||
return time.Now()
|
||||
}
|
||||
|
||||
func (portal *Portal) handleRemoteReactionSync(ctx context.Context, source *UserLogin, evt RemoteReactionSync) {
|
||||
log := zerolog.Ctx(ctx)
|
||||
eventTS := getEventTS(evt)
|
||||
targetMessage, err := portal.getTargetMessagePart(ctx, evt)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("Failed to get target message for reaction")
|
||||
return
|
||||
} else if targetMessage == nil {
|
||||
// TODO use deterministic event ID as target if applicable?
|
||||
log.Warn().Msg("Target message for reaction not found")
|
||||
return
|
||||
}
|
||||
var existingReactions []*database.Reaction
|
||||
if partTargeter, ok := evt.(RemoteEventWithTargetPart); ok {
|
||||
existingReactions, err = portal.Bridge.DB.Reaction.GetAllToMessagePart(ctx, evt.GetTargetMessage(), partTargeter.GetTargetMessagePart())
|
||||
} else {
|
||||
existingReactions, err = portal.Bridge.DB.Reaction.GetAllToMessage(ctx, evt.GetTargetMessage())
|
||||
}
|
||||
existing := make(map[networkid.UserID]map[networkid.EmojiID]*database.Reaction)
|
||||
for _, existingReaction := range existingReactions {
|
||||
if existing[existingReaction.SenderID] == nil {
|
||||
existing[existingReaction.SenderID] = make(map[networkid.EmojiID]*database.Reaction)
|
||||
}
|
||||
existing[existingReaction.SenderID][existingReaction.EmojiID] = existingReaction
|
||||
}
|
||||
|
||||
doAddReaction := func(new *BackfillReaction) MatrixAPI {
|
||||
intent := portal.GetIntentFor(ctx, new.Sender, source, RemoteEventReactionSync)
|
||||
portal.sendConvertedReaction(
|
||||
ctx, new.Sender.Sender, intent, targetMessage, new.EmojiID, new.Emoji,
|
||||
new.Timestamp, new.DBMetadata, new.ExtraContent,
|
||||
func(z *zerolog.Event) *zerolog.Event {
|
||||
return z.
|
||||
Any("reaction_sender_id", new.Sender).
|
||||
Time("reaction_ts", new.Timestamp)
|
||||
},
|
||||
)
|
||||
return intent
|
||||
}
|
||||
doRemoveReaction := func(old *database.Reaction, intent MatrixAPI) {
|
||||
if intent == nil && old.SenderMXID != "" {
|
||||
intent, err = portal.getIntentForMXID(ctx, old.SenderMXID)
|
||||
if err != nil {
|
||||
log.Err(err).
|
||||
Stringer("reaction_sender_mxid", old.SenderMXID).
|
||||
Msg("Failed to get intent for removing reaction")
|
||||
}
|
||||
}
|
||||
if intent == nil {
|
||||
log.Warn().
|
||||
Str("reaction_sender_id", string(old.SenderID)).
|
||||
Stringer("reaction_sender_mxid", old.SenderMXID).
|
||||
Msg("Didn't find intent for removing reaction, using bridge bot")
|
||||
intent = portal.Bridge.Bot
|
||||
}
|
||||
_, err = intent.SendMessage(ctx, portal.MXID, event.EventRedaction, &event.Content{
|
||||
Parsed: &event.RedactionEventContent{
|
||||
Redacts: old.MXID,
|
||||
},
|
||||
}, &MatrixSendExtra{Timestamp: eventTS})
|
||||
if err != nil {
|
||||
log.Err(err).Msg("Failed to redact old reaction")
|
||||
}
|
||||
}
|
||||
doOverwriteReaction := func(new *BackfillReaction, old *database.Reaction) {
|
||||
intent := doAddReaction(new)
|
||||
doRemoveReaction(old, intent)
|
||||
}
|
||||
|
||||
newData := evt.GetReactions()
|
||||
for userID, reactions := range newData.Users {
|
||||
existingUserReactions := existing[userID]
|
||||
delete(existing, userID)
|
||||
for _, reaction := range reactions.Reactions {
|
||||
if reaction.Timestamp.IsZero() {
|
||||
reaction.Timestamp = eventTS
|
||||
}
|
||||
existingReaction, ok := existingUserReactions[reaction.EmojiID]
|
||||
if ok {
|
||||
delete(existingUserReactions, reaction.EmojiID)
|
||||
if reaction.EmojiID != "" {
|
||||
continue
|
||||
}
|
||||
doOverwriteReaction(reaction, existingReaction)
|
||||
} else {
|
||||
doAddReaction(reaction)
|
||||
}
|
||||
}
|
||||
if reactions.HasAllReactions {
|
||||
for _, existingReaction := range existingUserReactions {
|
||||
doRemoveReaction(existingReaction, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
if newData.HasAllUsers {
|
||||
for _, userReactions := range existing {
|
||||
for _, existingReaction := range userReactions {
|
||||
doRemoveReaction(existingReaction, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (portal *Portal) handleRemoteReaction(ctx context.Context, source *UserLogin, evt RemoteReaction) {
|
||||
log := zerolog.Ctx(ctx)
|
||||
targetMessage, err := portal.getTargetMessagePart(ctx, evt)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue