reactions: use ReactionSync event
Signed-off-by: Sumner Evans <sumner.evans@automattic.com>
This commit is contained in:
@@ -11,7 +11,7 @@ require (
|
|||||||
go.uber.org/zap v1.27.0
|
go.uber.org/zap v1.27.0
|
||||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
|
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
|
||||||
golang.org/x/net v0.27.0
|
golang.org/x/net v0.27.0
|
||||||
maunium.net/go/mautrix v0.19.1-0.20240805194656-9fffe6e54d7e
|
maunium.net/go/mautrix v0.19.1-0.20240806155836-f6b0feab9566
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
|||||||
@@ -112,8 +112,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
maunium.net/go/mauflag v1.0.0 h1:YiaRc0tEI3toYtJMRIfjP+jklH45uDHtT80nUamyD4M=
|
maunium.net/go/mauflag v1.0.0 h1:YiaRc0tEI3toYtJMRIfjP+jklH45uDHtT80nUamyD4M=
|
||||||
maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA=
|
maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA=
|
||||||
maunium.net/go/mautrix v0.19.1-0.20240805194656-9fffe6e54d7e h1:OE04/iUnv8oG7UvjzMk40vkkrJvXs3NfbgvdJjAbsFg=
|
maunium.net/go/mautrix v0.19.1-0.20240806155836-f6b0feab9566 h1:3cp7ffpnUyViQDaXoPvw0Pq+0ax4toN4J4OLPLJs59Q=
|
||||||
maunium.net/go/mautrix v0.19.1-0.20240805194656-9fffe6e54d7e/go.mod h1:ZWyxoQxRTBxzWIMs0kQCVogZIY0clTu33h102veCT/Q=
|
maunium.net/go/mautrix v0.19.1-0.20240806155836-f6b0feab9566/go.mod h1:ZWyxoQxRTBxzWIMs0kQCVogZIY0clTu33h102veCT/Q=
|
||||||
nhooyr.io/websocket v1.8.11 h1:f/qXNc2/3DpoSZkHt1DQu6rj4zGC8JmkkLkWss0MgN0=
|
nhooyr.io/websocket v1.8.11 h1:f/qXNc2/3DpoSZkHt1DQu6rj4zGC8JmkkLkWss0MgN0=
|
||||||
nhooyr.io/websocket v1.8.11/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c=
|
nhooyr.io/websocket v1.8.11/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c=
|
||||||
rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY=
|
rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY=
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import (
|
|||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/gotd/td/telegram"
|
"github.com/gotd/td/telegram"
|
||||||
"github.com/gotd/td/telegram/updates"
|
"github.com/gotd/td/telegram/updates"
|
||||||
@@ -39,8 +38,6 @@ type TelegramClient struct {
|
|||||||
client *telegram.Client
|
client *telegram.Client
|
||||||
clientCancel context.CancelFunc
|
clientCancel context.CancelFunc
|
||||||
|
|
||||||
reactionMessageLocks map[int]*sync.Mutex
|
|
||||||
|
|
||||||
appConfig map[string]any
|
appConfig map[string]any
|
||||||
appConfigHash int
|
appConfigHash int
|
||||||
|
|
||||||
@@ -151,7 +148,6 @@ func NewTelegramClient(ctx context.Context, tc *TelegramConnector, login *bridge
|
|||||||
UpdateHandler: updatesManager,
|
UpdateHandler: updatesManager,
|
||||||
})
|
})
|
||||||
client.clientCancel, err = connectTelegramClient(ctx, client.client)
|
client.clientCancel, err = connectTelegramClient(ctx, client.client)
|
||||||
client.reactionMessageLocks = map[int]*sync.Mutex{}
|
|
||||||
|
|
||||||
client.telegramFmtParams = &telegramfmt.FormatParams{
|
client.telegramFmtParams = &telegramfmt.FormatParams{
|
||||||
GetUserInfoByID: func(ctx context.Context, id int64) (telegramfmt.UserInfo, error) {
|
GetUserInfoByID: func(ctx context.Context, id int64) (telegramfmt.UserInfo, error) {
|
||||||
|
|||||||
+57
-144
@@ -4,8 +4,6 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"slices"
|
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gotd/td/tg"
|
"github.com/gotd/td/tg"
|
||||||
@@ -13,6 +11,7 @@ import (
|
|||||||
"maunium.net/go/mautrix/bridgev2"
|
"maunium.net/go/mautrix/bridgev2"
|
||||||
"maunium.net/go/mautrix/bridgev2/database"
|
"maunium.net/go/mautrix/bridgev2/database"
|
||||||
"maunium.net/go/mautrix/bridgev2/networkid"
|
"maunium.net/go/mautrix/bridgev2/networkid"
|
||||||
|
"maunium.net/go/mautrix/bridgev2/simplevent"
|
||||||
|
|
||||||
"go.mau.fi/mautrix-telegram/pkg/connector/emojis"
|
"go.mau.fi/mautrix-telegram/pkg/connector/emojis"
|
||||||
"go.mau.fi/mautrix-telegram/pkg/connector/ids"
|
"go.mau.fi/mautrix-telegram/pkg/connector/ids"
|
||||||
@@ -314,14 +313,6 @@ func (t *TelegramClient) handleTelegramReactions(ctx context.Context, msg *tg.Me
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := t.reactionMessageLocks[msg.ID]; !ok {
|
|
||||||
t.reactionMessageLocks[msg.ID] = &sync.Mutex{}
|
|
||||||
}
|
|
||||||
t.reactionMessageLocks[msg.ID].Lock()
|
|
||||||
defer t.reactionMessageLocks[msg.ID].Unlock()
|
|
||||||
|
|
||||||
isFull := len(reactionsList) == totalCount
|
|
||||||
reactions := map[networkid.UserID][]tg.MessagePeerReaction{}
|
|
||||||
var customEmojiIDs []int64
|
var customEmojiIDs []int64
|
||||||
for _, reaction := range reactionsList {
|
for _, reaction := range reactionsList {
|
||||||
if e, ok := reaction.Reaction.(*tg.ReactionCustomEmoji); ok {
|
if e, ok := reaction.Reaction.(*tg.ReactionCustomEmoji); ok {
|
||||||
@@ -330,19 +321,68 @@ func (t *TelegramClient) handleTelegramReactions(ctx context.Context, msg *tg.Me
|
|||||||
log.Error().Type("reaction", reaction.Reaction).Msg("unknown reaction type")
|
log.Error().Type("reaction", reaction.Reaction).Msg("unknown reaction type")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if p, ok := reaction.PeerID.(*tg.PeerUser); !ok {
|
customEmojis, err := t.transferEmojisToMatrix(ctx, customEmojiIDs)
|
||||||
|
if err != nil {
|
||||||
|
log.Err(err).Msg("failed to transfer emojis")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
isFull := len(reactionsList) == totalCount
|
||||||
|
users := map[networkid.UserID]*bridgev2.ReactionSyncUser{}
|
||||||
|
for _, reaction := range reactionsList {
|
||||||
|
peer, ok := reaction.PeerID.(*tg.PeerUser)
|
||||||
|
if !ok {
|
||||||
log.Error().Type("peer_id", reaction.PeerID).Msg("unknown peer type")
|
log.Error().Type("peer_id", reaction.PeerID).Msg("unknown peer type")
|
||||||
return
|
return
|
||||||
} else {
|
|
||||||
reactions[ids.MakeUserID(p.UserID)] = append(reactions[ids.MakeUserID(p.UserID)], reaction)
|
|
||||||
}
|
}
|
||||||
|
userID := ids.MakeUserID(peer.UserID)
|
||||||
|
reactionLimit, err := t.getReactionLimit(ctx, userID)
|
||||||
|
if err != nil {
|
||||||
|
reactionLimit = 1
|
||||||
|
log.Err(err).Int64("id", peer.UserID).Msg("failed to get reaction limit")
|
||||||
|
}
|
||||||
|
if _, ok := users[userID]; !ok {
|
||||||
|
users[userID] = &bridgev2.ReactionSyncUser{HasAllReactions: isFull, MaxCount: reactionLimit}
|
||||||
|
}
|
||||||
|
|
||||||
|
var emojiID networkid.EmojiID
|
||||||
|
var emoji string
|
||||||
|
if r, ok := reaction.Reaction.(*tg.ReactionCustomEmoji); ok {
|
||||||
|
emojiID = ids.MakeEmojiIDFromDocumentID(r.DocumentID)
|
||||||
|
emoji = customEmojis[emojiID]
|
||||||
|
} else if r, ok := reaction.Reaction.(*tg.ReactionEmoji); ok {
|
||||||
|
emojiID = ids.MakeEmojiIDFromEmoticon(r.Emoticon)
|
||||||
|
emoji = r.Emoticon
|
||||||
|
} else {
|
||||||
|
log.Error().Type("reaction_type", reaction.Reaction).Msg("invalid reaction type")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
users[userID].Reactions = append(users[userID].Reactions, &bridgev2.BackfillReaction{
|
||||||
|
Timestamp: time.Unix(int64(reaction.Date), 0),
|
||||||
|
Sender: bridgev2.EventSender{
|
||||||
|
IsFromMe: reaction.My,
|
||||||
|
SenderLogin: ids.MakeUserLoginID(peer.UserID),
|
||||||
|
Sender: userID,
|
||||||
|
},
|
||||||
|
EmojiID: emojiID,
|
||||||
|
Emoji: emoji,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
err = t.handleTelegramParsedReactionsLocked(ctx, dbMsg, reactions, customEmojiIDs, isFull, nil, nil)
|
t.main.Bridge.QueueRemoteEvent(t.userLogin, &simplevent.ReactionSync{
|
||||||
if err != nil {
|
EventMeta: simplevent.EventMeta{
|
||||||
log.Err(err).Msg("failed to handle reactions")
|
Type: bridgev2.RemoteEventReactionSync,
|
||||||
}
|
LogContext: func(c zerolog.Context) zerolog.Context {
|
||||||
|
return c.Str("message_id", string(msg.ID))
|
||||||
|
},
|
||||||
|
PortalKey: dbMsg.Room,
|
||||||
|
},
|
||||||
|
TargetMessage: dbMsg.ID,
|
||||||
|
Reactions: &bridgev2.ReactionSyncData{Users: users, HasAllUsers: isFull},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TelegramClient) inputPeerForPortalID(ctx context.Context, portalID networkid.PortalID) (tg.InputPeerClass, error) {
|
func (t *TelegramClient) inputPeerForPortalID(ctx context.Context, portalID networkid.PortalID) (tg.InputPeerClass, error) {
|
||||||
@@ -451,130 +491,3 @@ func (t *TelegramClient) transferEmojisToMatrix(ctx context.Context, customEmoji
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TelegramClient) handleTelegramParsedReactionsLocked(ctx context.Context, msg *database.Message, reactions map[networkid.UserID][]tg.MessagePeerReaction, customEmojiIDs []int64, isFull bool, onlyUserID *networkid.UserID, timestamp *time.Time) error {
|
|
||||||
customEmojis, err := t.transferEmojisToMatrix(ctx, customEmojiIDs)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
existingReactions, err := t.main.Bridge.DB.Reaction.GetAllToMessage(ctx, msg.ID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var removed []*database.Reaction
|
|
||||||
for _, existing := range existingReactions {
|
|
||||||
if onlyUserID != nil && existing.SenderID != *onlyUserID {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var matched bool
|
|
||||||
reactions[existing.SenderID], matched, err = reactionsFilter(reactions[existing.SenderID], existing)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
} else if !matched {
|
|
||||||
if isFull {
|
|
||||||
removed = append(removed, existing)
|
|
||||||
} else if reactionLimit, err := t.getReactionLimit(ctx, existing.SenderID); err != nil {
|
|
||||||
return err
|
|
||||||
} else if len(reactions[existing.SenderID]) >= reactionLimit {
|
|
||||||
removed = append(removed, existing)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for sender, reactions := range reactions {
|
|
||||||
senderID, err := ids.ParseUserID(sender)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, reaction := range reactions {
|
|
||||||
var emojiID networkid.EmojiID
|
|
||||||
var emoji string
|
|
||||||
if r, ok := reaction.Reaction.(*tg.ReactionCustomEmoji); ok {
|
|
||||||
emojiID = ids.MakeEmojiIDFromDocumentID(r.DocumentID)
|
|
||||||
emoji = customEmojis[emojiID]
|
|
||||||
} else if r, ok := reaction.Reaction.(*tg.ReactionEmoji); ok {
|
|
||||||
emojiID = ids.MakeEmojiIDFromEmoticon(r.Emoticon)
|
|
||||||
emoji = r.Emoticon
|
|
||||||
} else {
|
|
||||||
return fmt.Errorf("invalid reaction type %T", reaction.Reaction)
|
|
||||||
}
|
|
||||||
|
|
||||||
evt := &bridgev2.SimpleRemoteEvent[any]{
|
|
||||||
Type: bridgev2.RemoteEventReaction,
|
|
||||||
LogContext: func(c zerolog.Context) zerolog.Context {
|
|
||||||
return c.
|
|
||||||
Any("reaction", reaction.Reaction).
|
|
||||||
Str("sender_id", string(sender)).
|
|
||||||
Str("message_id", string(msg.ID))
|
|
||||||
},
|
|
||||||
Sender: bridgev2.EventSender{
|
|
||||||
IsFromMe: reaction.My,
|
|
||||||
SenderLogin: ids.MakeUserLoginID(senderID),
|
|
||||||
Sender: sender,
|
|
||||||
},
|
|
||||||
PortalKey: msg.Room,
|
|
||||||
TargetMessage: msg.ID,
|
|
||||||
EmojiID: emojiID,
|
|
||||||
Emoji: emoji,
|
|
||||||
}
|
|
||||||
if timestamp != nil {
|
|
||||||
evt.Timestamp = *timestamp
|
|
||||||
}
|
|
||||||
t.main.Bridge.QueueRemoteEvent(t.userLogin, evt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, r := range removed {
|
|
||||||
senderID, err := ids.ParseUserID(r.SenderID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
evt := &bridgev2.SimpleRemoteEvent[any]{
|
|
||||||
Type: bridgev2.RemoteEventReactionRemove,
|
|
||||||
LogContext: func(c zerolog.Context) zerolog.Context {
|
|
||||||
return c.
|
|
||||||
Str("emoji_id", string(r.EmojiID)).
|
|
||||||
Str("sender_id", string(r.SenderID)).
|
|
||||||
Str("message_id", string(msg.ID))
|
|
||||||
},
|
|
||||||
Sender: bridgev2.EventSender{
|
|
||||||
IsFromMe: t.userID == r.SenderID,
|
|
||||||
SenderLogin: ids.MakeUserLoginID(senderID),
|
|
||||||
Sender: r.SenderID,
|
|
||||||
},
|
|
||||||
PortalKey: msg.Room,
|
|
||||||
TargetMessage: r.MessageID,
|
|
||||||
EmojiID: r.EmojiID,
|
|
||||||
}
|
|
||||||
if timestamp != nil {
|
|
||||||
evt.Timestamp = *timestamp
|
|
||||||
}
|
|
||||||
t.main.Bridge.QueueRemoteEvent(t.userLogin, evt)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func reactionsFilter(reactions []tg.MessagePeerReaction, existing *database.Reaction) (newReactions []tg.MessagePeerReaction, matched bool, err error) {
|
|
||||||
if len(reactions) == 0 {
|
|
||||||
return nil, false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
documentID, emoticon, err := ids.ParseEmojiID(existing.EmojiID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
newReactions = slices.DeleteFunc(reactions, func(r tg.MessagePeerReaction) bool {
|
|
||||||
if rce, ok := r.Reaction.(*tg.ReactionCustomEmoji); ok {
|
|
||||||
return documentID == rce.DocumentID
|
|
||||||
} else if r, ok := r.Reaction.(*tg.ReactionEmoji); ok {
|
|
||||||
return emoticon == r.Emoticon
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
return newReactions, len(newReactions) < len(reactions), nil
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user