Skip to content

Commit

Permalink
reactions: support deletions
Browse files Browse the repository at this point in the history
Signed-off-by: Sumner Evans <sumner.evans@automattic.com>
  • Loading branch information
sumnerevans committed Jul 8, 2024
1 parent 9dbb083 commit 6704a3a
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 59 deletions.
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,6 @@ github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg=
github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
go.mau.fi/util v0.5.1-0.20240702170310-bd1da3c069eb h1:VZPo2pvfjNj6fkFv5e9FyTYx96BLwwYNA19WYaY+KN8=
go.mau.fi/util v0.5.1-0.20240702170310-bd1da3c069eb/go.mod h1:DsJzUrJAG53lCZnnYvq9/mOyLuPScWwYhvETiTrpdP4=
go.mau.fi/zeroconfig v0.1.2 h1:DKOydWnhPMn65GbXZOafgkPm11BvFashZWLct0dGFto=
go.mau.fi/zeroconfig v0.1.2/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70=
go.mau.fi/zerozap v0.1.1 h1:mxE/dW4wtkqBYOXOEEzXldk5qKB+ahsZXjoTGnvEhZQ=
Expand Down
52 changes: 28 additions & 24 deletions pkg/connector/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"errors"
"fmt"
"strconv"
"strings"
"sync"

Expand All @@ -24,14 +23,19 @@ import (
)

type TelegramClient struct {
main *TelegramConnector
loginID int64
userLogin *bridgev2.UserLogin
client *telegram.Client
clientCancel context.CancelFunc
msgConv *msgconv.MessageConverter
main *TelegramConnector
telegramUserID int64
loginID networkid.UserLoginID
userID networkid.UserID
userLogin *bridgev2.UserLogin
client *telegram.Client
clientCancel context.CancelFunc
msgConv *msgconv.MessageConverter

reactionMessageLocks map[int]*sync.Mutex

appConfig map[string]any
appConfigHash int
}

var (
Expand Down Expand Up @@ -74,22 +78,24 @@ func (u UpdateDispatcher) Handle(ctx context.Context, updates tg.UpdatesClass) e
}

func NewTelegramClient(ctx context.Context, tc *TelegramConnector, login *bridgev2.UserLogin) (*TelegramClient, error) {
loginID, err := strconv.ParseInt(string(login.ID), 10, 64)
telegramUserID, err := ids.ParseUserLoginID(login.ID)
if err != nil {
return nil, err
}

log := zerolog.Ctx(ctx).With().
Str("component", "telegram_client").
Int64("login_id", loginID).
Str("user_login_id", string(login.ID)).
Logger()

zaplog := zap.New(zerozap.New(log))

client := TelegramClient{
main: tc,
loginID: loginID,
userLogin: login,
main: tc,
telegramUserID: telegramUserID,
loginID: login.ID,
userID: networkid.UserID(login.ID),
userLogin: login,
}
dispatcher := UpdateDispatcher{
UpdateDispatcher: tg.NewUpdateDispatcher(),
Expand All @@ -101,7 +107,7 @@ func NewTelegramClient(ctx context.Context, tc *TelegramConnector, login *bridge
dispatcher.OnDeleteMessages(client.onDeleteMessages)
dispatcher.OnEditMessage(client.onMessageEdit)

store := tc.Store.GetScopedStore(loginID)
store := tc.Store.GetScopedStore(telegramUserID)

updatesManager := updates.New(updates.Config{
OnChannelTooLong: func(channelID int64) {
Expand All @@ -123,7 +129,7 @@ func NewTelegramClient(ctx context.Context, tc *TelegramConnector, login *bridge
client.clientCancel, err = connectTelegramClient(ctx, client.client)
client.reactionMessageLocks = map[int]*sync.Mutex{}
go func() {
err = updatesManager.Run(ctx, client.client.API(), loginID, updates.AuthOptions{})
err = updatesManager.Run(ctx, client.client.API(), telegramUserID, updates.AuthOptions{})
if err != nil {
log.Err(err).Msg("updates manager error")
client.clientCancel()
Expand Down Expand Up @@ -165,9 +171,6 @@ func connectTelegramClient(ctx context.Context, client *telegram.Client) (contex
return cancel, nil
}




func (t *TelegramClient) Connect(ctx context.Context) (err error) {
t.clientCancel, err = connectTelegramClient(ctx, t.client)
return
Expand Down Expand Up @@ -213,8 +216,8 @@ func (t *TelegramClient) GetChatInfo(ctx context.Context, portal *bridgev2.Porta
{
EventSender: bridgev2.EventSender{
IsFromMe: true,
SenderLogin: ids.MakeUserLoginID(t.loginID),
Sender: ids.MakeUserID(t.loginID),
SenderLogin: t.loginID,
Sender: t.userID,
},
},
}
Expand Down Expand Up @@ -250,7 +253,7 @@ func (t *TelegramClient) GetChatInfo(ctx context.Context, portal *bridgev2.Porta
for _, user := range fullChat.Users {
memberList.Members = append(memberList.Members, bridgev2.ChatMember{
EventSender: bridgev2.EventSender{
IsFromMe: user.GetID() == t.loginID,
IsFromMe: user.GetID() == t.telegramUserID,
SenderLogin: ids.MakeUserLoginID(user.GetID()),
Sender: ids.MakeUserID(user.GetID()),
},
Expand Down Expand Up @@ -316,10 +319,11 @@ func (t *TelegramClient) getUserInfoFromTelegramUser(user *tg.User) (*bridgev2.U

name := util.FormatFullName(user.FirstName, user.LastName)
return &bridgev2.UserInfo{
IsBot: &user.Bot,
Name: &name,
Avatar: avatar,
Identifiers: identifiers,
IsBot: &user.Bot,
Name: &name,
Avatar: avatar,
Identifiers: identifiers,
ExtraUpdates: bridgev2.SimpleMetadataUpdater[*bridgev2.Ghost]("fi.mau.telegram.is_premium", user.Premium),
}, nil
}

Expand Down
4 changes: 4 additions & 0 deletions pkg/connector/ids/ids.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ func ParseUserID(userID networkid.UserID) (int64, error) {
return strconv.ParseInt(string(userID), 10, 64)
}

func ParseUserLoginID(userID networkid.UserLoginID) (int64, error) {
return strconv.ParseInt(string(userID), 10, 64)
}

func MakeUserLoginID(userID int64) networkid.UserLoginID {
return networkid.UserLoginID(strconv.FormatInt(userID, 10))
}
Expand Down
77 changes: 62 additions & 15 deletions pkg/connector/matrix.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ func (t *TelegramClient) HandleMatrixMessage(ctx context.Context, msg *bridgev2.
ID: ids.MakeMessageID(tgMessageID),
MXID: msg.Event.ID,
Room: networkid.PortalKey{ID: msg.Portal.ID},
SenderID: ids.MakeUserID(t.loginID),
SenderID: t.userID,
Timestamp: time.Unix(int64(tgDate), 0),
},
}
Expand All @@ -182,9 +182,8 @@ func (t *TelegramClient) HandleMatrixMessageRemove(ctx context.Context, msg *bri
func (t *TelegramClient) PreHandleMatrixReaction(ctx context.Context, msg *bridgev2.MatrixReaction) (bridgev2.MatrixReactionPreResponse, error) {
var resp bridgev2.MatrixReactionPreResponse

sender := ids.MakeUserID(t.loginID)
var maxReactions int
maxReactions, err := t.getReactionLimit(ctx, sender)
maxReactions, err := t.getReactionLimit(ctx, t.userID)
if err != nil {
return resp, err
}
Expand All @@ -205,13 +204,23 @@ func (t *TelegramClient) PreHandleMatrixReaction(ctx context.Context, msg *bridg
}

return bridgev2.MatrixReactionPreResponse{
SenderID: sender,
SenderID: t.userID,
EmojiID: emojiID,
Emoji: variationselector.FullyQualify(msg.Content.RelatesTo.Key),
MaxReactions: maxReactions,
}, nil
}

func (t *TelegramClient) appendEmojiID(reactionList []tg.ReactionClass, emojiID networkid.EmojiID) ([]tg.ReactionClass, error) {
if documentID, emoticon, err := ids.ParseEmojiID(emojiID); err != nil {
return nil, err
} else if documentID > 0 {
return append(reactionList, &tg.ReactionCustomEmoji{DocumentID: documentID}), nil
} else {
return append(reactionList, &tg.ReactionEmoji{Emoticon: emoticon}), nil
}
}

func (t *TelegramClient) HandleMatrixReaction(ctx context.Context, msg *bridgev2.MatrixReaction) (reaction *database.Reaction, err error) {
peer, err := ids.InputPeerForPortalID(msg.Portal.ID)
if err != nil {
Expand All @@ -222,25 +231,63 @@ func (t *TelegramClient) HandleMatrixReaction(ctx context.Context, msg *bridgev2
return nil, err
}

req := tg.MessagesSendReactionRequest{
Peer: peer,
AddToRecent: true,
MsgID: targetMessageID,
var newReactions []tg.ReactionClass
for _, existing := range msg.ExistingReactionsToKeep {
newReactions, err = t.appendEmojiID(newReactions, existing.EmojiID)
if err != nil {
return nil, err
}
}
if documentID, emoticon, err := ids.ParseEmojiID(msg.PreHandleResp.EmojiID); err != nil {
newReactions, err = t.appendEmojiID(newReactions, msg.PreHandleResp.EmojiID)
if err != nil {
return nil, err
} else if documentID > 0 {
req.SetReaction([]tg.ReactionClass{&tg.ReactionCustomEmoji{DocumentID: documentID}})
} else {
req.SetReaction([]tg.ReactionClass{&tg.ReactionEmoji{Emoticon: emoticon}})
}

_, err = t.client.API().MessagesSendReaction(ctx, &req)
_, err = t.client.API().MessagesSendReaction(ctx, &tg.MessagesSendReactionRequest{
Peer: peer,
AddToRecent: true,
MsgID: targetMessageID,
Reaction: newReactions,
})
return &database.Reaction{}, err
}

func (t *TelegramClient) HandleMatrixReactionRemove(ctx context.Context, msg *bridgev2.MatrixReactionRemove) error {
panic("unimplemented reaction remove")
peer, err := ids.InputPeerForPortalID(msg.Portal.ID)
if err != nil {
return err
}

var newReactions []tg.ReactionClass

if maxReactions, err := t.getReactionLimit(ctx, t.userID); err != nil {
return err
} else if maxReactions > 1 {
existing, err := t.main.Bridge.DB.Reaction.GetAllToMessageBySender(ctx, msg.TargetReaction.MessageID, msg.TargetReaction.SenderID)
if err != nil {
return err
}
for _, existing := range existing {
if msg.TargetReaction.EmojiID != existing.EmojiID {
newReactions, err = t.appendEmojiID(newReactions, existing.EmojiID)
if err != nil {
return err
}
}
}
}

messageID, err := ids.ParseMessageID(msg.TargetReaction.MessageID)
if err != nil {
return err
}
_, err = t.client.API().MessagesSendReaction(ctx, &tg.MessagesSendReactionRequest{
Peer: peer,
AddToRecent: true,
MsgID: messageID,
Reaction: newReactions,
})
return err
}

func (t *TelegramClient) HandleMatrixReadReceipt(ctx context.Context, msg *bridgev2.MatrixReadReceipt) error {
Expand Down
64 changes: 46 additions & 18 deletions pkg/connector/telegram.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"go.mau.fi/mautrix-telegram/pkg/connector/emojis"
"go.mau.fi/mautrix-telegram/pkg/connector/ids"
"go.mau.fi/mautrix-telegram/pkg/connector/media"
"go.mau.fi/mautrix-telegram/pkg/connector/tljson"
"go.mau.fi/mautrix-telegram/pkg/connector/util"
)

Expand Down Expand Up @@ -119,8 +120,8 @@ type messageWithSender interface {
func (t *TelegramClient) getEventSender(msg messageWithSender) (sender bridgev2.EventSender) {
if msg.GetOut() {
sender.IsFromMe = true
sender.SenderLogin = ids.MakeUserLoginID(t.loginID)
sender.Sender = ids.MakeUserID(t.loginID)
sender.SenderLogin = t.loginID
sender.Sender = t.userID
} else if f, ok := msg.GetFromID(); ok {
switch from := f.(type) {
case *tg.PeerUser:
Expand Down Expand Up @@ -160,7 +161,7 @@ func (t *TelegramClient) onUserName(ctx context.Context, e tg.Entities, update *

func (t *TelegramClient) onDeleteMessages(ctx context.Context, e tg.Entities, update *tg.UpdateDeleteMessages) error {
for _, messageID := range update.Messages {
parts, err := t.main.Bridge.DB.Message.GetAllPartsByID(ctx, ids.MakeUserLoginID(t.loginID), ids.MakeMessageID(messageID))
parts, err := t.main.Bridge.DB.Message.GetAllPartsByID(ctx, t.loginID, ids.MakeMessageID(messageID))
if err != nil {
return err
}
Expand Down Expand Up @@ -244,7 +245,7 @@ func (t *TelegramClient) handleTelegramReactions(ctx context.Context, msg *tg.Me
// self.log.warning(f"Can see reaction list in channel ({data!s})")
// # return

dbMsg, err := t.main.Bridge.DB.Message.GetFirstPartByID(ctx, ids.MakeUserLoginID(t.loginID), ids.MakeMessageID(msg.ID))
dbMsg, err := t.main.Bridge.DB.Message.GetFirstPartByID(ctx, t.loginID, ids.MakeMessageID(msg.ID))
if err != nil {
return err
} else if dbMsg == nil {
Expand All @@ -253,7 +254,7 @@ func (t *TelegramClient) handleTelegramReactions(ctx context.Context, msg *tg.Me

if len(reactionsList) < totalCount {
if user, ok := msg.PeerID.(*tg.PeerUser); ok {
reactionsList = splitDMReactionCounts(msg.Reactions.Results, user.UserID, t.loginID)
reactionsList = splitDMReactionCounts(msg.Reactions.Results, user.UserID, t.telegramUserID)
// } else if t.isBot {
// // Can't fetch exact reaction senders as a bot
// return
Expand Down Expand Up @@ -317,13 +318,44 @@ func splitDMReactionCounts(res []tg.ReactionCount, theirUserID, myUserID int64)
return
}

func (t *TelegramClient) getReactionLimit(ctx context.Context, sender networkid.UserID) (int, error) {
// TODO implement this correctly (probably need to put something into metadata)
// ghost, err := t.main.Bridge.GetGhostByID(ctx, sender)
// if err != nil {
// return 0, err
// }
return 1, nil
func (t *TelegramClient) getAppConfigCached(ctx context.Context) (map[string]any, error) {
if t.appConfig == nil {
cfg, err := t.client.API().HelpGetAppConfig(ctx, t.appConfigHash)
if err != nil {
return nil, err
}
appConfig, ok := cfg.(*tg.HelpAppConfig)
if !ok {
return nil, fmt.Errorf("failed to get app config: unexpected type %T", appConfig)
}
parsedConfig, err := tljson.Parse(appConfig.Config)
if err != nil {
return nil, err
}
t.appConfig, ok = parsedConfig.(map[string]any)
if !ok {
return nil, fmt.Errorf("failed to parse app config: unexpected type %T", t.appConfig)
}
t.appConfigHash = appConfig.Hash
}
return t.appConfig, nil
}

func (t *TelegramClient) getReactionLimit(ctx context.Context, sender networkid.UserID) (limit int, err error) {
config, err := t.getAppConfigCached(ctx)
if err != nil {
return 0, err
}

ghost, err := t.main.Bridge.GetGhostByID(ctx, sender)
if err != nil {
return 0, err
}
if isPremium, ok := ghost.Metadata.Extra["fi.mau.telegram.is_premium"].(bool); ok && isPremium {
return int(config["reactions_user_max_premium"].(float64)), nil
} else {
return int(config["reactions_user_max_default"].(float64)), nil
}
}

// TODO move this to emojis package
Expand Down Expand Up @@ -432,10 +464,6 @@ func (t *TelegramClient) handleTelegramParsedReactionsLocked(ctx context.Context
}

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 {
Expand All @@ -445,8 +473,8 @@ func (t *TelegramClient) handleTelegramParsedReactionsLocked(ctx context.Context
Str("message_id", string(msg.ID))
},
Sender: bridgev2.EventSender{
IsFromMe: t.loginID == senderID,
SenderLogin: ids.MakeUserLoginID(senderID),
IsFromMe: t.userID == r.SenderID,
SenderLogin: t.loginID,
Sender: r.SenderID,
},
PortalKey: msg.Room,
Expand Down
Loading

0 comments on commit 6704a3a

Please sign in to comment.