diff --git a/pkg/connector/client.go b/pkg/connector/client.go
index 0b78ed8c..8b6793a5 100644
--- a/pkg/connector/client.go
+++ b/pkg/connector/client.go
@@ -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() {
diff --git a/pkg/connector/config.go b/pkg/connector/config.go
index ce89b351..5baa5983 100644
--- a/pkg/connector/config.go
+++ b/pkg/connector/config.go
@@ -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"`
}
diff --git a/pkg/connector/msgconv/geouri.go b/pkg/connector/geouri.go
similarity index 67%
rename from pkg/connector/msgconv/geouri.go
rename to pkg/connector/geouri.go
index d32ec92c..8c4e848b 100644
--- a/pkg/connector/msgconv/geouri.go
+++ b/pkg/connector/geouri.go
@@ -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())
-}
diff --git a/pkg/connector/ids/media.go b/pkg/connector/ids/media.go
index e5f02ec4..98b14623 100644
--- a/pkg/connector/ids/media.go
+++ b/pkg/connector/ids/media.go
@@ -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
}
diff --git a/pkg/connector/matrix.go b/pkg/connector/matrix.go
index 742bad6d..93508c4a 100644
--- a/pkg/connector/matrix.go
+++ b/pkg/connector/matrix.go
@@ -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
diff --git a/pkg/connector/media/transfer.go b/pkg/connector/media/transfer.go
index 7de07ad1..c827e906 100644
--- a/pkg/connector/media/transfer.go
+++ b/pkg/connector/media/transfer.go
@@ -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
diff --git a/pkg/connector/msgconv/converter.go b/pkg/connector/msgconv/converter.go
deleted file mode 100644
index 940a6833..00000000
--- a/pkg/connector/msgconv/converter.go
+++ /dev/null
@@ -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,
- }
-}
diff --git a/pkg/connector/telegram.go b/pkg/connector/telegram.go
index 55c045f9..f8fb1c95 100644
--- a/pkg/connector/telegram.go
+++ b/pkg/connector/telegram.go
@@ -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")
diff --git a/pkg/connector/msgconv/tomatrix.go b/pkg/connector/tomatrix.go
similarity index 70%
rename from pkg/connector/msgconv/tomatrix.go
rename to pkg/connector/tomatrix.go
index 40a7b327..b5c1009d 100644
--- a/pkg/connector/msgconv/tomatrix.go
+++ b/pkg/connector/tomatrix.go
@@ -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 %s: %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: %s`, 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{