edits: bridge TG -> Matrix

Signed-off-by: Sumner Evans <sumner.evans@automattic.com>
This commit is contained in:
Sumner Evans
2024-07-19 12:47:55 -06:00
parent 5a3b52dff2
commit 314b2da99f
9 changed files with 211 additions and 185 deletions
-3
View File
@@ -19,7 +19,6 @@ import (
"go.mau.fi/mautrix-telegram/pkg/connector/ids"
"go.mau.fi/mautrix-telegram/pkg/connector/media"
"go.mau.fi/mautrix-telegram/pkg/connector/msgconv"
"go.mau.fi/mautrix-telegram/pkg/connector/store"
"go.mau.fi/mautrix-telegram/pkg/connector/util"
)
@@ -33,7 +32,6 @@ type TelegramClient struct {
userLogin *bridgev2.UserLogin
client *telegram.Client
clientCancel context.CancelFunc
msgConv *msgconv.MessageConverter
reactionMessageLocks map[int]*sync.Mutex
@@ -141,7 +139,6 @@ func NewTelegramClient(ctx context.Context, tc *TelegramConnector, login *bridge
Logger: zaplog,
UpdateHandler: updatesManager,
})
client.msgConv = msgconv.NewMessageConverter(client.client, tc.Bridge.Matrix, tc.Store, tc.Config.AnimatedSticker, tc.useDirectMedia)
client.clientCancel, err = connectTelegramClient(ctx, client.client)
client.reactionMessageLocks = map[int]*sync.Mutex{}
go func() {
+9 -9
View File
@@ -78,15 +78,11 @@ func (tg *TelegramConnector) ValidateConfig() error {
func (tg *TelegramConnector) GetDBMetaTypes() database.MetaTypes {
return database.MetaTypes{
Ghost: func() any {
return &GhostMetadata{}
},
Portal: nil,
Message: nil,
Reaction: nil,
UserLogin: func() any {
return &UserLoginMetadata{}
},
Ghost: func() any { return &GhostMetadata{} },
Portal: nil,
Message: func() any { return &MessageMetadata{} },
Reaction: nil,
UserLogin: func() any { return &UserLoginMetadata{} },
}
}
@@ -95,6 +91,10 @@ type GhostMetadata struct {
AccessHash int64 `json:"access_hash"`
}
type MessageMetadata struct {
ContentHash []byte `json:"content_hash,omitempty"`
}
type UserLoginMetadata struct {
Phone string `json:"phone"`
}
@@ -1,7 +1,6 @@
package msgconv
package connector
import (
"encoding/json"
"fmt"
"strconv"
"strings"
@@ -12,9 +11,6 @@ type GeoURI struct {
Long float64
}
var _ json.Unmarshaler = (*GeoURI)(nil)
var _ json.Marshaler = (*GeoURI)(nil)
func GeoURIFromLatLong(lat, long float64) GeoURI {
return GeoURI{lat, long}
}
@@ -42,19 +38,3 @@ func ParseGeoURI(uri string) (g GeoURI, err error) {
func (g GeoURI) URI() string {
return fmt.Sprintf("geo:%f,%f", g.Lat, g.Long)
}
func (g *GeoURI) UnmarshalJSON(data []byte) (err error) {
var uri string
err = json.Unmarshal(data, &uri)
if err != nil {
return err
}
geo, err := ParseGeoURI(uri)
g.Lat = geo.Lat
g.Long = geo.Long
return
}
func (g *GeoURI) MarshalJSON() ([]byte, error) {
return json.Marshal(g.URI())
}
+17 -8
View File
@@ -13,18 +13,20 @@ import (
// The format of the media ID is as follows (each character represents a single
// byte, |'s added for clarity):
//
// v|p|cccccccc|mmmmmmmm|T
// v|p|cccccccc|mmmmmmmm|T|MMMMMMMM
//
// v (int8) = binary encoding format version. Should be 0.
// p (byte) = the peer type of the Telegram chat ID
// cccccccc (int64) = the Telegram chat ID (big endian)
// mmmmmmmm (int64) = the Telegram message ID (big endian)
// T (byte) = 0 or 1 depending on whether it's a thumbnail
// T (byte) = 0 or 1 depending on whether it's a thumbnail (optional)
// MMMMMMMM (int64) = the Telegram media ID (big endian) (optional)
type DirectMediaInfo struct {
PeerType PeerType
ChatID int64
MessageID int64
Thumbnail bool
PeerType PeerType
ChatID int64
MessageID int64
Thumbnail bool
TelegramMediaID int64
}
func (m DirectMediaInfo) AsMediaID() (networkid.MediaID, error) {
@@ -39,6 +41,7 @@ func (m DirectMediaInfo) AsMediaID() (networkid.MediaID, error) {
} else {
mediaID = append(mediaID, 0x00)
}
mediaID = binary.BigEndian.AppendUint64(mediaID, uint64(m.TelegramMediaID)) // Telegram Message ID
return mediaID, nil
}
@@ -51,7 +54,10 @@ func ParseDirectMediaInfo(mediaID networkid.MediaID) (info DirectMediaInfo, err
err = fmt.Errorf("invalid version %d", mediaID[0])
return
}
if len(mediaID) != 18 && len(mediaID) != 19 {
// For compatibility with old media IDs that don't have the thumbnail flag
// and the Telegram media ID, we allow media IDs with 18, 19, or 27 bytes.
if len(mediaID) != 18 && len(mediaID) != 19 && len(mediaID) != 27 {
err = fmt.Errorf("invalid media ID")
return
}
@@ -61,8 +67,11 @@ func ParseDirectMediaInfo(mediaID networkid.MediaID) (info DirectMediaInfo, err
}
info.ChatID = int64(binary.BigEndian.Uint64(mediaID[2:]))
info.MessageID = int64(binary.BigEndian.Uint64(mediaID[10:]))
if len(mediaID) == 19 {
if len(mediaID) >= 19 {
info.Thumbnail = mediaID[18] == 1
}
if len(mediaID) >= 20 {
info.TelegramMediaID = int64(binary.BigEndian.Uint64(mediaID[19:]))
}
return
}
+12 -5
View File
@@ -2,6 +2,7 @@ package connector
import (
"context"
"crypto/sha256"
"fmt"
"strconv"
"strings"
@@ -21,7 +22,6 @@ import (
"go.mau.fi/mautrix-telegram/pkg/connector/emojis"
"go.mau.fi/mautrix-telegram/pkg/connector/ids"
"go.mau.fi/mautrix-telegram/pkg/connector/msgconv"
"go.mau.fi/mautrix-telegram/pkg/connector/waveform"
)
@@ -105,8 +105,8 @@ func (t *TelegramClient) HandleMatrixMessage(ctx context.Context, msg *bridgev2.
updates, err = builder.Media(ctx, media)
}
case event.MsgLocation:
var uri msgconv.GeoURI
uri, err = msgconv.ParseGeoURI(msg.Content.GeoURI)
var uri GeoURI
uri, err = ParseGeoURI(msg.Content.GeoURI)
if err != nil {
return nil, err
}
@@ -131,6 +131,9 @@ func (t *TelegramClient) HandleMatrixMessage(ctx context.Context, msg *bridgev2.
return nil, err
}
hasher := sha256.New()
hasher.Write([]byte(msg.Content.Body))
var tgMessageID, tgDate int
switch sentMessage := updates.(type) {
case *tg.UpdateShortSentMessage:
@@ -139,9 +142,12 @@ func (t *TelegramClient) HandleMatrixMessage(ctx context.Context, msg *bridgev2.
case *tg.Updates:
tgDate = sentMessage.Date
for _, u := range sentMessage.Updates {
if update, ok := u.(*tg.UpdateMessageID); ok {
switch update := u.(type) {
case *tg.UpdateMessageID:
tgMessageID = update.ID
break
case *tg.UpdateNewMessage:
msg := update.Message.(*tg.Message)
hasher.Write(mediaHashID(msg.Media))
}
}
if tgMessageID == 0 {
@@ -158,6 +164,7 @@ func (t *TelegramClient) HandleMatrixMessage(ctx context.Context, msg *bridgev2.
Room: networkid.PortalKey{ID: msg.Portal.ID},
SenderID: t.userID,
Timestamp: time.Unix(int64(tgDate), 0),
Metadata: &MessageMetadata{ContentHash: hasher.Sum(nil)},
},
}
return
+6 -5
View File
@@ -326,16 +326,17 @@ func (t *ReadyTransferer) Download(ctx context.Context) ([]byte, *event.FileInfo
}
// DirectDownloadURL returns the direct download URL for the media.
func (t *ReadyTransferer) DirectDownloadURL(ctx context.Context, portal *bridgev2.Portal, msgID int, thumbnail bool) (id.ContentURIString, *event.FileInfo, error) {
func (t *ReadyTransferer) DirectDownloadURL(ctx context.Context, portal *bridgev2.Portal, msgID int, thumbnail bool, telegramMediaID int64) (id.ContentURIString, *event.FileInfo, error) {
peerType, chatID, err := ids.ParsePortalID(portal.ID)
if err != nil {
return "", nil, err
}
mediaID, err := ids.DirectMediaInfo{
PeerType: peerType,
ChatID: chatID,
MessageID: int64(msgID),
Thumbnail: thumbnail,
PeerType: peerType,
ChatID: chatID,
MessageID: int64(msgID),
Thumbnail: thumbnail,
TelegramMediaID: telegramMediaID,
}.AsMediaID()
if err != nil {
return "", nil, err
-28
View File
@@ -1,28 +0,0 @@
package msgconv
import (
"github.com/gotd/td/telegram"
"maunium.net/go/mautrix/bridgev2"
"go.mau.fi/mautrix-telegram/pkg/connector/media"
"go.mau.fi/mautrix-telegram/pkg/connector/store"
)
type MessageConverter struct {
client *telegram.Client
connector bridgev2.MatrixConnector
store *store.Container
animatedStickerConfig media.AnimatedStickerConfig
useDirectMedia bool
}
func NewMessageConverter(client *telegram.Client, connector bridgev2.MatrixConnector, store *store.Container, animatedStickerConfig media.AnimatedStickerConfig, useDirectMedia bool) *MessageConverter {
return &MessageConverter{
client: client,
connector: connector,
store: store,
animatedStickerConfig: animatedStickerConfig,
useDirectMedia: useDirectMedia,
}
}
+42 -12
View File
@@ -1,6 +1,7 @@
package connector
import (
"bytes"
"context"
"fmt"
"slices"
@@ -58,7 +59,7 @@ func (t *TelegramClient) onUpdateNewMessage(ctx context.Context, update IGetMess
PortalKey: ids.MakePortalKey(msg.PeerID),
Data: msg,
CreatePortal: true,
ConvertMessageFunc: t.msgConv.ToMatrix,
ConvertMessageFunc: t.convertToMatrix,
Timestamp: time.Unix(int64(msg.Date), 0),
})
case *tg.MessageService:
@@ -209,30 +210,59 @@ func (t *TelegramClient) onEntityUpdate(ctx context.Context, e tg.Entities) erro
}
func (t *TelegramClient) onMessageEdit(ctx context.Context, update IGetMessage) error {
// fmt.Printf("message edit %+v\n", update)
msg, ok := update.GetMessage().(*tg.Message)
if !ok {
return fmt.Errorf("edit message is not *tg.Message")
}
// TODO do this async
if err := t.handleTelegramReactions(ctx, msg); err != nil {
return err
}
// t.main.Bridge.QueueRemoteEvent(t.userLogin, &bridgev2.SimpleRemoteEvent[*tg.Message]{
// Type: bridgev2.RemoteEventEdit,
// LogContext: func(c zerolog.Context) zerolog.Context {
// return c.
// Str("action", "edit message").
// Int("message_id", message.ID)
// },
// Sender: sender,
// PortalKey: ids.MakePortalID(message.PeerID),
// })
sender := t.getEventSender(msg)
t.main.Bridge.QueueRemoteEvent(t.userLogin, &bridgev2.SimpleRemoteEvent[*tg.Message]{
Type: bridgev2.RemoteEventEdit,
LogContext: func(c zerolog.Context) zerolog.Context {
return c.
Str("action", "edit_message").
Str("conversion_direction", "to_matrix").
Int("message_id", msg.ID)
},
ID: ids.MakeMessageID(msg.ID),
Sender: sender,
PortalKey: ids.MakePortalKey(msg.PeerID),
TargetMessage: ids.MakeMessageID(msg.ID),
Data: msg,
ConvertEditFunc: t.convertEdit,
Timestamp: time.Unix(int64(msg.EditDate), 0),
})
return nil
}
func (t *TelegramClient) convertEdit(ctx context.Context, portal *bridgev2.Portal, intent bridgev2.MatrixAPI, existing []*database.Message, msg *tg.Message) (*bridgev2.ConvertedEdit, error) {
converted, err := t.convertToMatrix(ctx, portal, intent, msg)
if err != nil {
return nil, err
}
if len(existing) != len(converted.Parts) {
return nil, fmt.Errorf("parts were added or removed in edit")
}
var ce bridgev2.ConvertedEdit
for i, part := range converted.Parts {
if bytes.Equal(existing[i].Metadata.(*MessageMetadata).ContentHash, part.DBMetadata.(*MessageMetadata).ContentHash) {
continue
}
ce.ModifiedParts = append(ce.ModifiedParts, part.ToEditPart(existing[i]))
}
return &ce, nil
}
func (t *TelegramClient) handleTelegramReactions(ctx context.Context, msg *tg.Message) error {
if _, set := msg.GetReactions(); !set {
// fmt.Printf("no reactions\n")
@@ -1,7 +1,9 @@
package msgconv
package connector
import (
"context"
"crypto/sha256"
"encoding/binary"
"fmt"
"html"
"strings"
@@ -30,15 +32,81 @@ type ttlable interface {
GetTTLSeconds() (value int, ok bool)
}
func (mc *MessageConverter) ToMatrix(ctx context.Context, portal *bridgev2.Portal, intent bridgev2.MatrixAPI, msg *tg.Message) (*bridgev2.ConvertedMessage, error) {
func mediaHashID(media tg.MessageMediaClass) []byte {
switch media := media.(type) {
case *tg.MessageMediaPhoto:
return binary.BigEndian.AppendUint64(nil, uint64(media.Photo.GetID()))
case *tg.MessageMediaDocument:
return binary.BigEndian.AppendUint64(nil, uint64(media.Document.GetID()))
}
return nil
}
func (c *TelegramClient) mediaToMatrix(ctx context.Context, portal *bridgev2.Portal, intent bridgev2.MatrixAPI, msg *tg.Message) (*bridgev2.ConvertedMessagePart, *database.DisappearingSetting, []byte, error) {
media, ok := msg.GetMedia()
if !ok {
return nil, nil, nil, nil
}
switch media.TypeID() {
case tg.MessageMediaWebPageTypeID:
// Already handled in the message handling
return nil, nil, nil, nil
case tg.MessageMediaUnsupportedTypeID:
return &bridgev2.ConvertedMessagePart{
ID: networkid.PartID("unsupported_media"),
Type: event.EventMessage,
Content: &event.MessageEventContent{
MsgType: event.MsgNotice,
Body: "This message is not supported on your version of Mautrix-Telegram. Please check https://github.com/mautrix/telegram or ask your bridge administrator about possible updates.",
},
Extra: map[string]any{
"fi.mau.telegram.unsupported": true,
},
}, nil, nil, nil
case tg.MessageMediaPhotoTypeID, tg.MessageMediaDocumentTypeID:
converted, disappearingSetting, err := c.convertMediaRequiringUpload(ctx, portal, intent, msg.ID, media)
return converted, disappearingSetting, mediaHashID(media), err
case tg.MessageMediaContactTypeID:
return c.convertContact(media), nil, nil, nil
case tg.MessageMediaGeoTypeID, tg.MessageMediaGeoLiveTypeID, tg.MessageMediaVenueTypeID:
location, err := convertLocation(media)
return location, nil, nil, err
case tg.MessageMediaPollTypeID:
return convertPoll(media), nil, nil, nil
case tg.MessageMediaDiceTypeID:
return convertDice(media), nil, nil, nil
case tg.MessageMediaGameTypeID:
return convertGame(media), nil, nil, nil
case tg.MessageMediaStoryTypeID, tg.MessageMediaInvoiceTypeID, tg.MessageMediaGiveawayTypeID, tg.MessageMediaGiveawayResultsTypeID:
// TODO: support these properly
return &bridgev2.ConvertedMessagePart{
ID: networkid.PartID("story"),
Type: event.EventMessage,
Content: &event.MessageEventContent{
MsgType: event.MsgNotice,
Body: fmt.Sprintf("%s are not yet supported. Open Telegram to view.", media.TypeName()),
},
Extra: map[string]any{
"fi.mau.telegram.unsupported": true,
"fi.mau.telegram.type_id": media.TypeID(),
},
}, nil, nil, nil
default:
return nil, nil, nil, fmt.Errorf("unsupported media type %T", media)
}
}
func (c *TelegramClient) convertToMatrix(ctx context.Context, portal *bridgev2.Portal, intent bridgev2.MatrixAPI, msg *tg.Message) (*bridgev2.ConvertedMessage, error) {
log := zerolog.Ctx(ctx).With().Str("conversion_direction", "to_matrix").Logger()
ctx = log.WithContext(ctx)
cm := &bridgev2.ConvertedMessage{}
hasher := sha256.New()
if len(msg.Message) > 0 {
var linkPreviews []*event.BeeperLinkPreview
if media, ok := msg.GetMedia(); ok && media.TypeID() == tg.MessageMediaWebPageTypeID {
preview, err := mc.webpageToBeeperLinkPreview(ctx, intent, media)
preview, err := c.webpageToBeeperLinkPreview(ctx, intent, media)
if err != nil {
return nil, err
} else if preview != nil {
@@ -46,81 +114,40 @@ func (mc *MessageConverter) ToMatrix(ctx context.Context, portal *bridgev2.Porta
}
}
hasher.Write([]byte(msg.Message))
// TODO formatting
// TODO combine with other media
cm.Parts = append(cm.Parts, &bridgev2.ConvertedMessagePart{
ID: networkid.PartID("caption"),
Type: event.EventMessage,
Content: &event.MessageEventContent{
MsgType: event.MsgText,
Body: msg.Message,
BeeperLinkPreviews: linkPreviews,
cm.Parts = []*bridgev2.ConvertedMessagePart{
{
ID: networkid.PartID("caption"),
Type: event.EventMessage,
Content: &event.MessageEventContent{
MsgType: event.MsgText,
Body: msg.Message,
BeeperLinkPreviews: linkPreviews,
},
},
})
}
if media, ok := msg.GetMedia(); ok {
switch media.TypeID() {
case tg.MessageMediaWebPageTypeID:
// Already handled above
case tg.MessageMediaUnsupportedTypeID:
cm.Parts = append(cm.Parts, &bridgev2.ConvertedMessagePart{
ID: networkid.PartID("unsupported_media"),
Type: event.EventMessage,
Content: &event.MessageEventContent{
MsgType: event.MsgNotice,
Body: "This message is not supported on your version of Mautrix-Telegram. Please check https://github.com/mautrix/telegram or ask your bridge administrator about possible updates.",
},
Extra: map[string]any{
"fi.mau.telegram.unsupported": true,
},
})
case tg.MessageMediaPhotoTypeID, tg.MessageMediaDocumentTypeID:
mediaPart, disappearingSetting, err := mc.convertMediaRequiringUpload(ctx, portal, intent, msg.ID, media)
if err != nil {
return nil, err
}
if disappearingSetting != nil {
cm.Disappear = *disappearingSetting
}
cm.Parts = append(cm.Parts, mediaPart)
case tg.MessageMediaContactTypeID:
cm.Parts = append(cm.Parts, mc.convertContact(media))
case tg.MessageMediaGeoTypeID, tg.MessageMediaGeoLiveTypeID, tg.MessageMediaVenueTypeID:
location, err := mc.convertLocation(media)
if err != nil {
return nil, err
}
cm.Parts = append(cm.Parts, location)
case tg.MessageMediaPollTypeID:
cm.Parts = append(cm.Parts, mc.convertPoll(media))
case tg.MessageMediaDiceTypeID:
cm.Parts = append(cm.Parts, mc.convertDice(media))
case tg.MessageMediaGameTypeID:
cm.Parts = append(cm.Parts, mc.convertGame(media))
case tg.MessageMediaStoryTypeID, tg.MessageMediaInvoiceTypeID, tg.MessageMediaGiveawayTypeID, tg.MessageMediaGiveawayResultsTypeID:
// TODO: support these properly
cm.Parts = append(cm.Parts, &bridgev2.ConvertedMessagePart{
ID: networkid.PartID("story"),
Type: event.EventMessage,
Content: &event.MessageEventContent{
MsgType: event.MsgNotice,
Body: fmt.Sprintf("%s are not yet supported. Open Telegram to view.", media.TypeName()),
},
Extra: map[string]any{
"fi.mau.telegram.unsupported": true,
"fi.mau.telegram.type_id": media.TypeID(),
},
})
default:
return nil, fmt.Errorf("unsupported media type %T", media)
}
}
mediaPart, disappearingSetting, mediaHashID, err := c.mediaToMatrix(ctx, portal, intent, msg)
if err != nil {
return nil, err
} else if mediaPart != nil {
hasher.Write(mediaHashID)
cm.Parts = append(cm.Parts, mediaPart)
cm.MergeCaption()
if disappearingSetting != nil {
cm.Disappear = *disappearingSetting
}
}
cm.Parts[0].DBMetadata = &MessageMetadata{ContentHash: hasher.Sum(nil)}
return cm, nil
}
func (mc *MessageConverter) webpageToBeeperLinkPreview(ctx context.Context, intent bridgev2.MatrixAPI, msgMedia tg.MessageMediaClass) (preview *event.BeeperLinkPreview, err error) {
func (c *TelegramClient) webpageToBeeperLinkPreview(ctx context.Context, intent bridgev2.MatrixAPI, msgMedia tg.MessageMediaClass) (preview *event.BeeperLinkPreview, err error) {
webpage, ok := msgMedia.(*tg.MessageMediaWebPage).Webpage.(*tg.WebPage)
if !ok {
return nil, nil
@@ -136,9 +163,9 @@ func (mc *MessageConverter) webpageToBeeperLinkPreview(ctx context.Context, inte
if pc, ok := webpage.GetPhoto(); ok && pc.TypeID() == tg.PhotoTypeID {
var fileInfo *event.FileInfo
preview.ImageURL, preview.ImageEncryption, fileInfo, err = media.NewTransferer(mc.client.API()).
preview.ImageURL, preview.ImageEncryption, fileInfo, err = media.NewTransferer(c.client.API()).
WithPhoto(pc).
Transfer(ctx, mc.store, intent)
Transfer(ctx, c.main.Store, intent)
if err != nil {
return nil, err
}
@@ -148,7 +175,7 @@ func (mc *MessageConverter) webpageToBeeperLinkPreview(ctx context.Context, inte
return preview, nil
}
func (mc *MessageConverter) convertMediaRequiringUpload(ctx context.Context, portal *bridgev2.Portal, intent bridgev2.MatrixAPI, msgID int, msgMedia tg.MessageMediaClass) (*bridgev2.ConvertedMessagePart, *database.DisappearingSetting, error) {
func (c *TelegramClient) convertMediaRequiringUpload(ctx context.Context, portal *bridgev2.Portal, intent bridgev2.MatrixAPI, msgID int, msgMedia tg.MessageMediaClass) (*bridgev2.ConvertedMessagePart, *database.DisappearingSetting, error) {
log := zerolog.Ctx(ctx).With().
Str("conversion_direction", "to_matrix").
Str("portal_id", string(portal.ID)).
@@ -157,10 +184,11 @@ func (mc *MessageConverter) convertMediaRequiringUpload(ctx context.Context, por
eventType := event.EventMessage
var partID networkid.PartID
var content event.MessageEventContent
var telegramMediaID int64
var isSticker, isAnimatedSticker bool
extra := map[string]any{}
transferer := media.NewTransferer(mc.client.API()).WithRoomID(portal.MXID)
transferer := media.NewTransferer(c.client.API()).WithRoomID(portal.MXID)
var mediaTransferer *media.ReadyTransferer
// Determine the filename and some other information
@@ -169,12 +197,14 @@ func (mc *MessageConverter) convertMediaRequiringUpload(ctx context.Context, por
partID = networkid.PartID("photo")
content.MsgType = event.MsgImage
content.Body = "image"
telegramMediaID = msgMedia.Photo.GetID()
mediaTransferer = transferer.WithPhoto(msgMedia.Photo)
case *tg.MessageMediaDocument:
document, ok := msgMedia.Document.(*tg.Document)
if !ok {
return nil, nil, fmt.Errorf("unrecognized document type %T", msgMedia.Document)
}
telegramMediaID = document.GetID()
partID = networkid.PartID("document")
content.MsgType = event.MsgFile
@@ -208,7 +238,7 @@ func (mc *MessageConverter) convertMediaRequiringUpload(ctx context.Context, por
transferer = transferer.WithImageSize(a)
case *tg.DocumentAttributeSticker:
isSticker = true
if mc.animatedStickerConfig.Target == "webm" {
if c.main.Config.AnimatedSticker.Target == "webm" {
content.MsgType = event.MsgVideo
} else {
eventType = event.EventSticker
@@ -238,13 +268,13 @@ func (mc *MessageConverter) convertMediaRequiringUpload(ctx context.Context, por
extraInfo["fi.mau.autoplay"] = true
extraInfo["fi.mau.hide_controls"] = true
extraInfo["fi.mau.no_audio"] = true
transferer = transferer.WithStickerConfig(mc.animatedStickerConfig)
transferer = transferer.WithStickerConfig(c.main.Config.AnimatedSticker)
case *tg.DocumentAttributeAnimated:
isAnimatedSticker = true
}
}
if isAnimatedSticker || (isSticker && mc.animatedStickerConfig.Target == "webm") {
if isAnimatedSticker || (isSticker && c.main.Config.AnimatedSticker.Target == "webm") {
if isAnimatedSticker {
extraInfo["fi.mau.telegram.gif"] = true
} else {
@@ -259,17 +289,17 @@ func (mc *MessageConverter) convertMediaRequiringUpload(ctx context.Context, por
var thumbnailInfo *event.FileInfo
var err error
thumbnailTransferer := media.NewTransferer(mc.client.API()).
thumbnailTransferer := media.NewTransferer(c.client.API()).
WithRoomID(portal.MXID).
WithDocument(document, true)
if mc.useDirectMedia {
thumbnailURL, thumbnailInfo, err = thumbnailTransferer.DirectDownloadURL(ctx, portal, msgID, true)
if c.main.useDirectMedia {
thumbnailURL, thumbnailInfo, err = thumbnailTransferer.DirectDownloadURL(ctx, portal, msgID, true, document.ID)
if err != nil {
log.Err(err).Msg("error getting direct download URL for thumbnail")
}
}
if thumbnailURL == "" {
thumbnailURL, thumbnailFile, thumbnailInfo, err = thumbnailTransferer.Transfer(ctx, mc.store, intent)
thumbnailURL, thumbnailFile, thumbnailInfo, err = thumbnailTransferer.Transfer(ctx, c.main.Store, intent)
if err != nil {
return nil, nil, fmt.Errorf("error transferring thumbnail: %w", err)
}
@@ -286,14 +316,14 @@ func (mc *MessageConverter) convertMediaRequiringUpload(ctx context.Context, por
}
var err error
if mc.useDirectMedia && (!isSticker || mc.animatedStickerConfig.Target == "disable") {
content.URL, content.Info, err = mediaTransferer.DirectDownloadURL(ctx, portal, msgID, false)
if c.main.useDirectMedia && (!isSticker || c.main.Config.AnimatedSticker.Target == "disable") {
content.URL, content.Info, err = mediaTransferer.DirectDownloadURL(ctx, portal, msgID, false, telegramMediaID)
if err != nil {
log.Err(err).Msg("error getting direct download URL for media")
}
}
if content.URL == "" {
content.URL, content.File, content.Info, err = mediaTransferer.Transfer(ctx, mc.store, intent)
content.URL, content.File, content.Info, err = mediaTransferer.Transfer(ctx, c.main.Store, intent)
if err != nil {
return nil, nil, fmt.Errorf("error transferring media: %w", err)
}
@@ -337,7 +367,7 @@ func (mc *MessageConverter) convertMediaRequiringUpload(ctx context.Context, por
}, disappearingSetting, nil
}
func (mc *MessageConverter) convertContact(media tg.MessageMediaClass) *bridgev2.ConvertedMessagePart {
func (c *TelegramClient) convertContact(media tg.MessageMediaClass) *bridgev2.ConvertedMessagePart {
contact := media.(*tg.MessageMediaContact)
name := util.FormatFullName(contact.FirstName, contact.LastName)
formattedPhone := fmt.Sprintf("+%s", strings.TrimPrefix(contact.PhoneNumber, "+"))
@@ -350,7 +380,7 @@ func (mc *MessageConverter) convertContact(media tg.MessageMediaClass) *bridgev2
content.Format = event.FormatHTML
content.FormattedBody = fmt.Sprintf(
`Shared contact info for <a href="https://matrix.to/#/%s">%s</a>: %s`,
mc.connector.GhostIntent(ids.MakeUserID(contact.UserID)).GetMXID(),
c.main.Bridge.Matrix.GhostIntent(ids.MakeUserID(contact.UserID)).GetMXID(),
html.EscapeString(name),
html.EscapeString(formattedPhone),
)
@@ -376,7 +406,7 @@ type hasGeo interface {
GetGeo() tg.GeoPointClass
}
func (mc *MessageConverter) convertLocation(media tg.MessageMediaClass) (*bridgev2.ConvertedMessagePart, error) {
func convertLocation(media tg.MessageMediaClass) (*bridgev2.ConvertedMessagePart, error) {
g, ok := media.(hasGeo)
if !ok || g.GetGeo().TypeID() != tg.GeoPointTypeID {
return nil, fmt.Errorf("location didn't have geo or geo is wrong type")
@@ -395,7 +425,7 @@ func (mc *MessageConverter) convertLocation(media tg.MessageMediaClass) (*bridge
}
geo := fmt.Sprintf("%f,%f", point.Lat, point.Long)
geoURI := GeoURIFromLatLong(point.Lat, point.Long)
geoURI := GeoURIFromLatLong(point.Lat, point.Long).URI()
body := fmt.Sprintf("%.4f° %s, %.4f° %s", point.Lat, latChar, point.Long, longChar)
url := fmt.Sprintf("https://maps.google.com/?q=%s", geo)
@@ -412,7 +442,7 @@ func (mc *MessageConverter) convertLocation(media tg.MessageMediaClass) (*bridge
}
extra["org.matrix.msc3488.location"] = map[string]any{
"uri": geoURI.URI(),
"uri": geoURI,
"description": note,
}
@@ -421,7 +451,7 @@ func (mc *MessageConverter) convertLocation(media tg.MessageMediaClass) (*bridge
Type: event.EventMessage,
Content: &event.MessageEventContent{
MsgType: event.MsgLocation,
GeoURI: geoURI.URI(),
GeoURI: geoURI,
Body: fmt.Sprintf("%s: %s\n%s", note, body, url),
Format: event.FormatHTML,
FormattedBody: fmt.Sprintf(`%s: <a href="%s">%s</a>`, note, url, body),
@@ -430,7 +460,7 @@ func (mc *MessageConverter) convertLocation(media tg.MessageMediaClass) (*bridge
}, nil
}
func (mc *MessageConverter) convertPoll(media tg.MessageMediaClass) *bridgev2.ConvertedMessagePart {
func convertPoll(media tg.MessageMediaClass) *bridgev2.ConvertedMessagePart {
// TODO (PLAT-25224) make this richer in the future once megabridge has support for polls
poll := media.(*tg.MessageMediaPoll)
@@ -453,7 +483,7 @@ func (mc *MessageConverter) convertPoll(media tg.MessageMediaClass) *bridgev2.Co
}
}
func (mc *MessageConverter) convertDice(media tg.MessageMediaClass) *bridgev2.ConvertedMessagePart {
func convertDice(media tg.MessageMediaClass) *bridgev2.ConvertedMessagePart {
roll := media.(*tg.MessageMediaDice)
var result string
@@ -524,7 +554,7 @@ func (mc *MessageConverter) convertDice(media tg.MessageMediaClass) *bridgev2.Co
}
}
func (mc *MessageConverter) convertGame(media tg.MessageMediaClass) *bridgev2.ConvertedMessagePart {
func convertGame(media tg.MessageMediaClass) *bridgev2.ConvertedMessagePart {
// TODO (PLAT-25562) provide a richer experience for the game
game := media.(*tg.MessageMediaGame)
return &bridgev2.ConvertedMessagePart{