converter: handle link previews TG -> Matrix

Signed-off-by: Sumner Evans <sumner.evans@automattic.com>
This commit is contained in:
Sumner Evans
2024-06-20 11:49:16 -06:00
parent b568ef8d8c
commit 891750592d
3 changed files with 102 additions and 29 deletions
+1 -2
View File
@@ -84,7 +84,7 @@ func (tc *TelegramConnector) Download(ctx context.Context, mediaID networkid.Med
var mimeType string
switch media := media.(type) {
case *tg.MessageMediaPhoto:
data, mimeType, err = download.DownloadPhoto(ctx, client.client.API(), media)
data, mimeType, err = download.DownloadPhotoMedia(ctx, client.client.API(), media)
case *tg.MessageMediaDocument:
document, ok := media.Document.(*tg.Document)
if !ok {
@@ -98,7 +98,6 @@ func (tc *TelegramConnector) Download(ctx context.Context, mediaID networkid.Med
// case *tg.MessageMediaContact: // messageMediaContact#70322949
// case *tg.MessageMediaUnsupported: // messageMediaUnsupported#9f84f49e
// case *tg.MessageMediaDocument: // messageMediaDocument#4cf4d72d
// case *tg.MessageMediaWebPage: // messageMediaWebPage#ddf10c3b
// case *tg.MessageMediaVenue: // messageMediaVenue#2ec0533f
// case *tg.MessageMediaGame: // messageMediaGame#fdb19008
// case *tg.MessageMediaInvoice: // messageMediaInvoice#f6a548d3
+15 -12
View File
@@ -10,7 +10,7 @@ import (
"github.com/gotd/td/tg"
)
func getLargestPhotoSize(sizes []tg.PhotoSizeClass) (largest tg.PhotoSizeClass) {
func GetLargestPhotoSize(sizes []tg.PhotoSizeClass) (largest tg.PhotoSizeClass) {
var maxSize int
for _, s := range sizes {
var currentSize int
@@ -35,17 +35,8 @@ func getLargestPhotoSize(sizes []tg.PhotoSizeClass) (largest tg.PhotoSizeClass)
return
}
func DownloadPhoto(ctx context.Context, client downloader.Client, media *tg.MessageMediaPhoto) (data []byte, mimeType string, err error) {
p, ok := media.GetPhoto()
if !ok {
return nil, "", fmt.Errorf("photo message sent without a photo")
}
photo, ok := p.(*tg.Photo)
if !ok {
return nil, "", fmt.Errorf("unrecognized photo type %T", p)
}
largest := getLargestPhotoSize(photo.GetSizes())
func DownloadPhoto(ctx context.Context, client downloader.Client, photo *tg.Photo) (data []byte, mimeType string, err error) {
largest := GetLargestPhotoSize(photo.GetSizes())
file := tg.InputPhotoFileLocation{
ID: photo.GetID(),
AccessHash: photo.GetAccessHash(),
@@ -81,3 +72,15 @@ func DownloadPhoto(ctx context.Context, client downloader.Client, media *tg.Mess
}
return buf.Bytes(), mimeType, nil
}
func DownloadPhotoMedia(ctx context.Context, client downloader.Client, media *tg.MessageMediaPhoto) (data []byte, mimeType string, err error) {
p, ok := media.GetPhoto()
if !ok {
return nil, "", fmt.Errorf("photo message sent without a photo")
}
photo, ok := p.(*tg.Photo)
if !ok {
return nil, "", fmt.Errorf("unrecognized photo type %T", p)
}
return DownloadPhoto(ctx, client, photo)
}
+86 -15
View File
@@ -3,6 +3,7 @@ package msgconv
import (
"context"
"fmt"
"slices"
"time"
"github.com/gotd/td/tg"
@@ -26,33 +27,105 @@ type ttlable interface {
GetTTLSeconds() (value int, ok bool)
}
func mediaRequiringUpload(media tg.MessageMediaClass) bool {
allowed := []uint32{
tg.MessageMediaPhotoTypeID,
tg.MessageMediaGeoTypeID,
tg.MessageMediaContactTypeID,
tg.MessageMediaDocumentTypeID,
tg.MessageMediaStoryTypeID,
}
return slices.Contains(allowed, media.TypeID())
}
func (mc *MessageConverter) ToMatrix(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{}
if msg.Message != "" {
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)
if err != nil {
return nil, err
} else if preview != nil {
linkPreviews = append(linkPreviews, preview)
}
}
// TODO formatting
cm.Parts = append(cm.Parts, &bridgev2.ConvertedMessagePart{
ID: networkid.PartID("caption"),
Type: event.EventMessage,
Content: &event.MessageEventContent{MsgType: event.MsgText, Body: msg.Message},
ID: networkid.PartID("caption"),
Type: event.EventMessage,
Content: &event.MessageEventContent{
MsgType: event.MsgText,
Body: msg.Message,
BeeperLinkPreviews: linkPreviews,
},
})
}
if media, ok := msg.GetMedia(); ok {
mediaParts, disappearingSetting, err := mc.convertMedia(ctx, portal, intent, msg.ID, media)
if err != nil {
return nil, err
switch {
case mediaRequiringUpload(media):
mediaParts, 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, mediaParts)
}
if disappearingSetting != nil {
cm.Disappear = *disappearingSetting
}
cm.Parts = append(cm.Parts, mediaParts)
}
return cm, nil
}
func (mc *MessageConverter) convertMedia(ctx context.Context, portal *bridgev2.Portal, intent bridgev2.MatrixAPI, msgID int, media tg.MessageMediaClass) (*bridgev2.ConvertedMessagePart, *database.DisappearingSetting, error) {
func (mc *MessageConverter) webpageToBeeperLinkPreview(ctx context.Context, intent bridgev2.MatrixAPI, media tg.MessageMediaClass) (*event.BeeperLinkPreview, error) {
webpage, ok := media.(*tg.MessageMediaWebPage).Webpage.(*tg.WebPage)
if !ok {
return nil, nil
}
preview := &event.BeeperLinkPreview{
MatchedURL: webpage.URL,
LinkPreview: event.LinkPreview{
Title: webpage.Title,
CanonicalURL: webpage.URL,
Description: webpage.Description,
},
}
if pc, ok := webpage.GetPhoto(); ok && pc.TypeID() == tg.PhotoTypeID {
photo := pc.(*tg.Photo)
for _, s := range photo.GetSizes() {
switch size := s.(type) {
case *tg.PhotoCachedSize:
preview.ImageWidth = size.GetW()
preview.ImageHeight = size.GetH()
case *tg.PhotoSizeProgressive:
preview.ImageWidth = size.GetW()
preview.ImageHeight = size.GetH()
}
}
data, mimeType, err := download.DownloadPhoto(ctx, mc.client.API(), photo)
if err != nil {
return nil, err
}
preview.ImageSize = len(data)
preview.ImageType = mimeType
preview.ImageURL, preview.ImageEncryption, err = intent.UploadMedia(ctx, "", data, "", mimeType)
if err != nil {
return nil, err
}
}
return preview, nil
}
func (mc *MessageConverter) convertMediaRequiringUpload(ctx context.Context, portal *bridgev2.Portal, intent bridgev2.MatrixAPI, msgID int, media tg.MessageMediaClass) (*bridgev2.ConvertedMessagePart, *database.DisappearingSetting, error) {
var partID networkid.PartID
var msgType event.MessageType
var filename string
@@ -97,7 +170,6 @@ func (mc *MessageConverter) convertMedia(ctx context.Context, portal *bridgev2.P
// case *tg.MessageMediaGeo: // messageMediaGeo#56e0d474
// case *tg.MessageMediaContact: // messageMediaContact#70322949
// case *tg.MessageMediaUnsupported: // messageMediaUnsupported#9f84f49e
// case *tg.MessageMediaWebPage: // messageMediaWebPage#ddf10c3b
// case *tg.MessageMediaVenue: // messageMediaVenue#2ec0533f
// case *tg.MessageMediaGame: // messageMediaGame#fdb19008
// case *tg.MessageMediaInvoice: // messageMediaInvoice#f6a548d3
@@ -146,7 +218,7 @@ func (mc *MessageConverter) convertMedia(ctx context.Context, portal *bridgev2.P
filename = "image" + exmime.ExtensionFromMimetype(mimeType)
}
data, mimeType, err = download.DownloadPhoto(ctx, mc.client.API(), media)
data, mimeType, err = download.DownloadPhotoMedia(ctx, mc.client.API(), media)
case *tg.MessageMediaDocument:
document, ok := media.Document.(*tg.Document)
if !ok {
@@ -160,7 +232,6 @@ func (mc *MessageConverter) convertMedia(ctx context.Context, portal *bridgev2.P
// case *tg.MessageMediaGeo: // messageMediaGeo#56e0d474
// case *tg.MessageMediaContact: // messageMediaContact#70322949
// case *tg.MessageMediaUnsupported: // messageMediaUnsupported#9f84f49e
// case *tg.MessageMediaWebPage: // messageMediaWebPage#ddf10c3b
// case *tg.MessageMediaVenue: // messageMediaVenue#2ec0533f
// case *tg.MessageMediaGame: // messageMediaGame#fdb19008
// case *tg.MessageMediaInvoice: // messageMediaInvoice#f6a548d3