reactions: support custom emojis

Signed-off-by: Sumner Evans <sumner.evans@automattic.com>
This commit is contained in:
Sumner Evans
2024-06-28 10:41:28 -06:00
parent 33dc5bad03
commit a63f264804
16 changed files with 244 additions and 228 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ require (
github.com/gotd/td v0.102.0
github.com/rs/zerolog v1.33.0
github.com/stretchr/testify v1.9.0
go.mau.fi/util v0.5.1-0.20240702170310-bd1da3c069eb
go.mau.fi/util v0.5.1-0.20240708233020-c2f9af6fecf8
go.mau.fi/zerozap v0.1.1
go.uber.org/zap v1.27.0
maunium.net/go/mautrix v0.19.0-beta.1.0.20240706124659-b4057a26c3ed
+2 -2
View File
@@ -67,8 +67,8 @@ 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/util v0.5.1-0.20240708233020-c2f9af6fecf8 h1:7ntkhSR0G/dIwAPjcoOoJz+bPne0gA4PypEn8euMMx0=
go.mau.fi/util v0.5.1-0.20240708233020-c2f9af6fecf8/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=
+1 -1
View File
@@ -119,7 +119,7 @@ func NewTelegramClient(ctx context.Context, tc *TelegramConnector, login *bridge
Logger: zaplog,
UpdateHandler: updatesManager,
})
client.msgConv = msgconv.NewMessageConverter(client.client, tc.Bridge.Matrix, tc.useDirectMedia)
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() {
+47
View File
@@ -0,0 +1,47 @@
package connector
import (
_ "embed"
"fmt"
up "go.mau.fi/util/configupgrade"
"maunium.net/go/mautrix/bridgev2"
"go.mau.fi/mautrix-telegram/pkg/connector/media"
)
var _ bridgev2.ConfigValidatingNetwork = (*TelegramConnector)(nil)
type TelegramConfig struct {
AppID int `yaml:"app_id"`
AppHash string `yaml:"app_hash"`
AnimatedSticker media.AnimatedStickerConfig `yaml:"animated_sticker"`
}
//go:embed example-config.yaml
var ExampleConfig string
func upgradeConfig(helper up.Helper) {
helper.Copy(up.Int, "app_id")
helper.Copy(up.Str, "app_hash")
helper.Copy(up.Str, "animated_sticker.target")
helper.Copy(up.Bool, "animated_sticker.convert_from_webm")
helper.Copy(up.Int, "animated_sticker.args.width")
helper.Copy(up.Int, "animated_sticker.args.height")
helper.Copy(up.Int, "animated_sticker.args.fps")
}
func (tg *TelegramConnector) GetConfig() (example string, data any, upgrader up.Upgrader) {
return ExampleConfig, tg.Config, up.SimpleUpgrader(upgradeConfig)
}
func (tg *TelegramConnector) ValidateConfig() error {
if tg.Config.AppID == 0 {
return fmt.Errorf("app_id is required")
}
if tg.Config.AppHash == "" {
return fmt.Errorf("app_hash is required")
}
return nil
}
-32
View File
@@ -18,21 +18,13 @@ package connector
import (
"context"
_ "embed"
"fmt"
up "go.mau.fi/util/configupgrade"
"go.mau.fi/util/dbutil"
"maunium.net/go/mautrix/bridgev2"
"go.mau.fi/mautrix-telegram/pkg/connector/store"
)
type TelegramConfig struct {
AppID int `yaml:"app_id"`
AppHash string `yaml:"app_hash"`
}
type TelegramConnector struct {
Bridge *bridgev2.Bridge
Config *TelegramConfig
@@ -42,7 +34,6 @@ type TelegramConnector struct {
}
var _ bridgev2.NetworkConnector = (*TelegramConnector)(nil)
var _ bridgev2.ConfigValidatingNetwork = (*TelegramConnector)(nil)
// var _ bridgev2.MaxFileSizeingNetwork = (*TelegramConnector)(nil)
@@ -53,7 +44,6 @@ func NewConnector() *TelegramConnector {
}
func (tg *TelegramConnector) Init(bridge *bridgev2.Bridge) {
// TODO
tg.Store = store.NewStore(bridge.DB.Database, dbutil.ZeroLogger(bridge.Log.With().Str("db_section", "telegram").Logger()))
tg.Bridge = bridge
}
@@ -67,28 +57,6 @@ func (tc *TelegramConnector) LoadUserLogin(ctx context.Context, login *bridgev2.
return
}
//go:embed example-config.yaml
var ExampleConfig string
func upgradeConfig(helper up.Helper) {
helper.Copy(up.Int, "app_id")
helper.Copy(up.Str, "app_hash")
}
func (tg *TelegramConnector) GetConfig() (example string, data any, upgrader up.Upgrader) {
return ExampleConfig, tg.Config, up.SimpleUpgrader(upgradeConfig)
}
func (tg *TelegramConnector) ValidateConfig() error {
if tg.Config.AppID == 0 {
return fmt.Errorf("app_id is required")
}
if tg.Config.AppHash == "" {
return fmt.Errorf("app_hash is required")
}
return nil
}
// TODO
// func (tg *TelegramConnector) SetMaxFileSize(maxSize int64) {
// }
+1 -1
View File
@@ -91,8 +91,8 @@ func (tc *TelegramConnector) Download(ctx context.Context, mediaID networkid.Med
return nil, fmt.Errorf("unrecognized document type %T", msgMedia.Document)
}
// Download the thumbnail for this media rather than the media itself.
if info.Thumbnail {
// Download the thumbnail for this media rather than the media itself.
_, _, largestThumbnail := media.GetLargestPhotoSize(document.Thumbs)
data, mimeType, err = media.DownloadFileLocation(ctx, client.client.API(), &tg.InputDocumentFileLocation{
ID: document.GetID(),
+1 -2
View File
@@ -31,8 +31,7 @@ func ConvertKnownEmojis(emojiIDs []int64) (result map[networkid.EmojiID]string,
result = map[networkid.EmojiID]string{}
for _, e := range emojiIDs {
if v, ok := reverseUnicodemojiPack[e]; ok {
emojiID := ids.MakeEmojiIDFromDocumentID(e)
result[emojiID] = v
result[ids.MakeEmojiIDFromDocumentID(e)] = v
} else {
remaining = append(remaining, e)
}
+15 -68
View File
@@ -1,72 +1,19 @@
# Get your own API keys at https://my.telegram.org/apps
app_id: 12345
app_hash: tjyd5yge35lbodk1xwzw2jstp90k55qz
# (Optional) Create your own bot at https://t.me/BotFather
bot_token: disabled
# Should the bridge request missed updates from Telegram when restarting?
catch_up: true
# Should incoming updates be handled sequentially to make sure order is preserved on Matrix?
sequential_updates: true
exit_on_update_error: false
# Telethon connection options.
connection:
# The timeout in seconds to be used when connecting.
timeout: 120
# How many times the reconnection should retry, either on the initial connection or when
# Telegram disconnects us. May be set to a negative or null value for infinite retries, but
# this is not recommended, since the program can get stuck in an infinite loop.
retries: 5
# The delay in seconds to sleep between automatic reconnections.
retry_delay: 1
# The threshold below which the library should automatically sleep on flood wait errors
# (inclusive). For instance, if a FloodWaitError for 17s occurs and flood_sleep_threshold
# is 20s, the library will sleep automatically. If the error was for 21s, it would raise
# the error instead. Values larger than a day (86400) will be changed to a day.
flood_sleep_threshold: 60
# How many times a request should be retried. Request are retried when Telegram is having
# internal issues, when there is a FloodWaitError less than flood_sleep_threshold, or when
# there's a migrate error. May take a negative or null value for infinite retries, but this
# is not recommended, since some requests can always trigger a call fail (such as searching
# for messages).
request_retries: 5
# Use IPv6 for Telethon connection
use_ipv6: false
# Device info sent to Telegram.
device_info:
# "auto" = OS name+version.
device_model: mautrix-telegram
# "auto" = Telethon version.
system_version: auto
# "auto" = mautrix-telegram version.
app_version: auto
lang_code: en
system_lang_code: en
# Custom server to connect to.
server:
# Set to true to use these server settings. If false, will automatically
# use production server assigned by Telegram. Set to false in production.
enabled: false
# The DC ID to connect to.
dc: 2
# The IP to connect to.
ip: 149.154.167.40
# The port to connect to. 443 may not work, 80 is better and both are equally secure.
port: 80
# Telethon proxy configuration.
# You must install PySocks from pip for proxies to work.
proxy:
# Allowed types: disabled, socks4, socks5, http, mtproxy
type: disabled
# Proxy IP address and port.
address: 127.0.0.1
port: 1080
# Whether or not to perform DNS resolving remotely. Only for socks/http proxies.
rdns: true
# Proxy authentication (optional). Put MTProxy secret in password field.
username: ""
password: ""
animated_sticker:
# Format to which animated stickers should be converted.
# disable - No conversion, send as-is (gzipped lottie)
# png - converts to non-animated png (fastest),
# gif - converts to animated gif
# webm - converts to webm video, requires ffmpeg executable with vp9 codec and webm container support
# webp - converts to animated webp, requires ffmpeg executable with webp codec/container support
target: gif
# Should video stickers be converted to the specified format as well?
convert_from_webm: false
# Arguments for converter. All converters take width and height.
args:
width: 256
height: 256
fps: 25 # only for webm, webp and gif (2, 5, 10, 20 or 25 recommended)
+2
View File
@@ -193,6 +193,8 @@ func (t *TelegramClient) PreHandleMatrixReaction(ctx context.Context, msg *bridg
if strings.HasPrefix(msg.Content.RelatesTo.Key, "mxc://") {
if file, err := t.main.Store.TelegramFile.GetByMXC(ctx, msg.Content.RelatesTo.Key); err != nil {
return resp, err
} else if file == nil {
return resp, fmt.Errorf("reaction MXC URI %s does not correspond with any known Telegram files", msg.Content.RelatesTo.Key)
} else if documentID, err := strconv.ParseInt(string(file.LocationID), 10, 64); err != nil {
return resp, err
} else {
+3 -3
View File
@@ -9,10 +9,10 @@ import (
"github.com/gotd/td/tg"
)
func DownloadFileLocation(ctx context.Context, client downloader.Client, file tg.InputFileLocationClass) (data []byte, mimeType string, err error) {
// TODO convert to streaming? stream to file?
func DownloadFileLocation(ctx context.Context, client downloader.Client, loc tg.InputFileLocationClass) (data []byte, mimeType string, err error) {
// TODO convert entire function to streaming? Maybe at least stream to file?
var buf bytes.Buffer
storageFileTypeClass, err := downloader.NewDownloader().Download(client, file).Stream(ctx, &buf)
storageFileTypeClass, err := downloader.NewDownloader().Download(client, loc).Stream(ctx, &buf)
if err != nil {
return nil, "", err
}
+102 -20
View File
@@ -9,37 +9,119 @@ import (
"maunium.net/go/mautrix/bridgev2"
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
"go.mau.fi/util/lottie"
"go.mau.fi/mautrix-telegram/pkg/connector/store"
)
// LocationToID converts a Telegram [tg.Document],
// getLocationID converts a Telegram [tg.Document],
// [tg.InputDocumentFileLocation], [tg.InputPeerPhotoFileLocation],
// [tg.InputFileLocation], or [tg.InputPhotoFileLocation] into a key for use in
// the telegram_file table.
func LocationToID(location any) (id string) {
switch location := location.(type) {
// [tg.InputFileLocation], or [tg.InputPhotoFileLocation] into a [LocationID]
// for use in the telegram_file table.
func getLocationID(loc any) (locID store.TelegramFileLocationID) {
var id string
switch location := loc.(type) {
case *tg.Document:
return fmt.Sprintf("%d", location.ID)
id = fmt.Sprintf("%d", location.ID)
case *tg.InputDocumentFileLocation:
return fmt.Sprintf("%d-%s", location.ID, location.ThumbSize)
id = fmt.Sprintf("%d-%s", location.ID, location.ThumbSize)
case *tg.InputPhotoFileLocation:
return fmt.Sprintf("%d-%s", location.ID, location.ThumbSize)
id = fmt.Sprintf("%d-%s", location.ID, location.ThumbSize)
case *tg.InputFileLocation:
return fmt.Sprintf("%d-%d", location.VolumeID, location.LocalID)
id = fmt.Sprintf("%d-%d", location.VolumeID, location.LocalID)
case *tg.InputPeerPhotoFileLocation:
return fmt.Sprintf("%d", location.PhotoID)
id = fmt.Sprintf("%d", location.PhotoID)
default:
panic(fmt.Errorf("unknown location type %T", location))
}
return store.TelegramFileLocationID(id)
}
func TransferToMatrix(ctx context.Context, roomID id.RoomID, client downloader.Client, intent bridgev2.MatrixAPI, file tg.InputFileLocationClass, filenameOpt ...string) (id.ContentURIString, *event.EncryptedFileInfo, error) {
data, mimeType, err := DownloadFileLocation(ctx, client, file)
if err != nil {
return "", nil, fmt.Errorf("downloading file failed: %w", err)
}
var filename string
if len(filenameOpt) > 0 {
filename = filenameOpt[0]
}
return intent.UploadMedia(ctx, roomID, data, filename, mimeType)
type AnimatedStickerConfig struct {
Target string `yaml:"target"`
ConvertFromWebm bool `yaml:"convert_from_webm"`
Args struct {
Width int `yaml:"width"`
Height int `yaml:"height"`
FPS int `yaml:"fps"`
} `yaml:"args"`
}
func (c AnimatedStickerConfig) TGSConvert() bool {
return c.Target == "gif" || c.Target == "png"
}
func (c AnimatedStickerConfig) WebmConvert() bool {
return c.ConvertFromWebm && c.Target != "webm"
}
type Transferer struct {
RoomID id.RoomID
Filename string
IsSticker bool
Config AnimatedStickerConfig
}
func NewTransferer(cfg AnimatedStickerConfig) *Transferer {
return &Transferer{Config: cfg}
}
func (t *Transferer) WithRoomID(roomID id.RoomID) *Transferer {
t.RoomID = roomID
return t
}
func (t *Transferer) WithFilename(filename string) *Transferer {
t.Filename = filename
return t
}
func (t *Transferer) WithIsSticker(isSticker bool) *Transferer {
t.IsSticker = isSticker
return t
}
func (t *Transferer) Transfer(ctx context.Context, store *store.Container, client downloader.Client, intent bridgev2.MatrixAPI, loc tg.InputFileLocationClass) (mxc id.ContentURIString, encryptedFileInfo *event.EncryptedFileInfo, size int, mimeType string, err error) {
locationID := getLocationID(loc)
if file, err := store.TelegramFile.GetByLocationID(ctx, locationID); err != nil {
return "", nil, 0, "", fmt.Errorf("failed to search for Telegram file by location ID: %w", err)
} else if file != nil {
return file.MXC, nil, file.Size, file.MIMEType, nil
}
var data []byte
data, mimeType, err = DownloadFileLocation(ctx, client, loc)
if err != nil {
return "", nil, 0, "", fmt.Errorf("downloading file failed: %w", err)
}
if t.IsSticker {
if lottie.Supported() && t.Config.TGSConvert() && mimeType == "application/x-gzip" {
data, err = lottie.ConvertBytes(ctx, data, t.Config.Target, t.Config.Args.Width, t.Config.Args.Height, fmt.Sprintf("%d", t.Config.Args.FPS))
if err != nil {
return "", nil, 0, "", err
}
mimeType = fmt.Sprintf("image/%s", t.Config.Target)
// TODO support ffmpeg conversion
// } else if ffmpeg.Supported() && t.Config.WebmConvert() && mimeType == "video/webm" {
}
}
mxcURI, encryptedFileInfo, err := intent.UploadMedia(ctx, t.RoomID, data, t.Filename, mimeType)
if err != nil {
return "", nil, 0, "", err
}
if len(mxcURI) > 0 {
file := store.TelegramFile.New()
file.LocationID = locationID
file.MXC = mxcURI
file.Size = len(data)
file.MIMEType = mimeType
// TODO width, height, thumbnail?
if err = file.Insert(ctx); err != nil {
return "", nil, 0, "", fmt.Errorf("failed to insert Telegram file into database: %w", err)
}
}
return mxcURI, encryptedFileInfo, len(data), mimeType, nil
}
+15 -4
View File
@@ -3,15 +3,26 @@ 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
client *telegram.Client
connector bridgev2.MatrixConnector
store *store.Container
animatedStickerConfig media.AnimatedStickerConfig
useDirectMedia bool
}
func NewMessageConverter(client *telegram.Client, connector bridgev2.MatrixConnector, useDirectMedia bool) *MessageConverter {
return &MessageConverter{client: client, connector: connector, useDirectMedia: useDirectMedia}
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,
}
}
+15 -14
View File
@@ -208,12 +208,14 @@ func (mc *MessageConverter) convertMediaRequiringUpload(ctx context.Context, por
}
if info.ThumbnailInfo.ThumbnailURL == "" {
info.ThumbnailInfo.ThumbnailURL, info.ThumbnailInfo.ThumbnailFile, err = media.TransferToMatrix(ctx, portal.MXID, mc.client.API(), intent, &tg.InputDocumentFileLocation{
ID: document.GetID(),
AccessHash: document.GetAccessHash(),
FileReference: document.GetFileReference(),
ThumbSize: largestThumbnail.GetType(),
})
info.ThumbnailInfo.ThumbnailURL, info.ThumbnailInfo.ThumbnailFile, info.ThumbnailInfo.Size, info.ThumbnailInfo.MimeType, err = media.NewTransferer(mc.animatedStickerConfig).
WithRoomID(portal.MXID).
Transfer(ctx, mc.store, mc.client.API(), intent, &tg.InputDocumentFileLocation{
ID: document.GetID(),
AccessHash: document.GetAccessHash(),
FileReference: document.GetFileReference(),
ThumbSize: largestThumbnail.GetType(),
})
if err != nil {
return nil, nil, err
}
@@ -256,24 +258,23 @@ func (mc *MessageConverter) convertMediaRequiringUpload(ctx context.Context, por
if mxcURI == "" {
var data []byte
var mimeType string
var err error
switch msgMedia := msgMedia.(type) {
case *tg.MessageMediaPhoto:
// TODO convert to Transfer
data, _, _, info.MimeType, err = media.DownloadPhotoMedia(ctx, mc.client.API(), msgMedia)
if _, ok := msgMedia.GetTTLSeconds(); ok {
filename = "disappearing_image" + exmime.ExtensionFromMimetype(mimeType)
filename = "disappearing_image" + exmime.ExtensionFromMimetype(info.MimeType)
} else {
filename = "image" + exmime.ExtensionFromMimetype(mimeType)
filename = "image" + exmime.ExtensionFromMimetype(info.MimeType)
}
data, _, _, mimeType, err = media.DownloadPhotoMedia(ctx, mc.client.API(), msgMedia)
case *tg.MessageMediaDocument:
document, ok := msgMedia.Document.(*tg.Document)
if !ok {
return nil, nil, fmt.Errorf("unrecognized document type %T", msgMedia.Document)
}
mimeType = document.GetMimeType()
info.MimeType = document.GetMimeType()
// TODO convert to Transfer
data, err = media.DownloadDocument(ctx, mc.client.API(), document)
default:
return nil, nil, fmt.Errorf("unhandled media type %T", msgMedia)
@@ -282,7 +283,7 @@ func (mc *MessageConverter) convertMediaRequiringUpload(ctx context.Context, por
return nil, nil, err
}
mxcURI, encryptedFileInfo, err = intent.UploadMedia(ctx, portal.MXID, data, filename, mimeType)
mxcURI, encryptedFileInfo, err = intent.UploadMedia(ctx, portal.MXID, data, filename, info.MimeType)
if err != nil {
return nil, nil, err
}
+10 -52
View File
@@ -2,23 +2,14 @@ package store
import (
"context"
"database/sql"
"encoding/json"
"time"
"go.mau.fi/util/dbutil"
"maunium.net/go/mautrix/id"
)
const (
insertTelegramFileQuery = `
INSERT INTO telegram_file (
id, mxc, mime_type, was_converted, timestamp, size, width, height, thumbnail, decryption_info)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
`
getTelegramFileSelect = `
SELECT id, mxc, mime_type, was_converted, timestamp, size, width, height, thumbnail, decryption_info
FROM telegram_file
`
insertTelegramFileQuery = "INSERT INTO telegram_file (id, mxc, mime_type, size) VALUES ($1, $2, $3, $4)"
getTelegramFileSelect = "SELECT id, mxc, mime_type, size FROM telegram_file "
getTelegramFileByLocationIDQuery = getTelegramFileSelect + "WHERE id=$1"
getTelegramFileByMXCQuery = getTelegramFileSelect + "WHERE mxc=$1"
)
@@ -32,16 +23,10 @@ type TelegramFileLocationID string
type TelegramFile struct {
qh *dbutil.QueryHelper[*TelegramFile]
LocationID TelegramFileLocationID
MXC string
MimeType string
WasConverted bool
Timestamp time.Time
Size int64
Width int
Height int
ThumbnailID string
DecryptionInfo json.RawMessage
LocationID TelegramFileLocationID
MXC id.ContentURIString
MIMEType string
Size int
}
var _ dbutil.DataStruct[*TelegramFile] = (*TelegramFile)(nil)
@@ -50,7 +35,7 @@ func newTelegramFile(qh *dbutil.QueryHelper[*TelegramFile]) *TelegramFile {
return &TelegramFile{qh: qh}
}
func (fq *TelegramFileQuery) GetByLocationID(ctx context.Context, locationID string) (*TelegramFile, error) {
func (fq *TelegramFileQuery) GetByLocationID(ctx context.Context, locationID TelegramFileLocationID) (*TelegramFile, error) {
return fq.QueryOne(ctx, getTelegramFileByLocationIDQuery, locationID)
}
@@ -59,18 +44,7 @@ func (fq *TelegramFileQuery) GetByMXC(ctx context.Context, mxc string) (*Telegra
}
func (f *TelegramFile) sqlVariables() []any {
return []any{
f.LocationID,
f.MXC,
f.MimeType,
f.WasConverted,
f.Timestamp.UnixMilli(),
f.Size,
f.Width,
f.Height,
f.ThumbnailID,
f.DecryptionInfo,
}
return []any{f.LocationID, f.MXC, f.MIMEType, f.Size}
}
func (f *TelegramFile) Insert(ctx context.Context) error {
@@ -78,21 +52,5 @@ func (f *TelegramFile) Insert(ctx context.Context) error {
}
func (f *TelegramFile) Scan(row dbutil.Scannable) (*TelegramFile, error) {
var thumbnailID sql.NullString
var timestamp int64
err := row.Scan(
&f.LocationID,
&f.MXC,
&f.MimeType,
&f.WasConverted,
&timestamp,
&f.Size,
&f.Width,
&f.Height,
&thumbnailID,
&f.DecryptionInfo,
)
f.Timestamp = time.UnixMilli(timestamp)
f.ThumbnailID = thumbnailID.String
return f, err
return f, row.Scan(&f.LocationID, &f.MXC, &f.MIMEType, &f.Size)
}
+4 -12
View File
@@ -32,16 +32,8 @@ CREATE TABLE telegram_channel_access_hashes (
);
CREATE TABLE telegram_file (
id TEXT PRIMARY KEY,
mxc TEXT NOT NULL,
mime_type TEXT,
was_converted BOOLEAN NOT NULL DEFAULT false,
timestamp BIGINT NOT NULL DEFAULT 0,
size BIGINT,
width INTEGER,
height INTEGER,
thumbnail TEXT,
decryption_info jsonb,
FOREIGN KEY (thumbnail) REFERENCES telegram_file(id)
ON UPDATE CASCADE ON DELETE SET NULL
id TEXT PRIMARY KEY,
mxc TEXT NOT NULL,
mime_type TEXT,
size BIGINT
);
+25 -16
View File
@@ -15,6 +15,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/util"
)
@@ -225,7 +226,6 @@ func (t *TelegramClient) handleTelegramReactions(ctx context.Context, msg *tg.Me
if _, set := msg.GetReactions(); !set {
return nil
}
fmt.Printf("handle reactions %+v\n", msg.Reactions)
var totalCount int
for _, r := range msg.Reactions.Results {
totalCount += r.Count
@@ -253,6 +253,8 @@ 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)
// TODO
// } else if t.isBot {
// // Can't fetch exact reaction senders as a bot
// return
@@ -325,25 +327,34 @@ func (t *TelegramClient) getReactionLimit(ctx context.Context, sender networkid.
return 1, nil
}
// TODO move this to emojis package
func (t *TelegramClient) transferEmojisToMatrix(ctx context.Context, customEmojiIDs []int64) (result map[networkid.EmojiID]string, err error) {
result, customEmojiIDs = emojis.ConvertKnownEmojis(customEmojiIDs)
for _, customEmojiID := range customEmojiIDs {
fmt.Printf("customEmojiID %d\n", customEmojiID)
locationID := fmt.Sprintf("%d", customEmojiID)
if file, err := t.main.Store.TelegramFile.GetByLocationID(ctx, locationID); err != nil {
return nil, fmt.Errorf("failed to search for Telegram file by location ID")
} else if file == nil {
// TODO download shit
} else {
result[ids.MakeEmojiIDFromDocumentID(customEmojiID)] = file.MXC
if len(customEmojiIDs) > 0 {
customEmojiDocuments, err := t.client.API().MessagesGetCustomEmojiDocuments(ctx, customEmojiIDs)
if err != nil {
return nil, err
}
for _, customEmojiDocument := range customEmojiDocuments {
document := customEmojiDocument.(*tg.Document)
mxcURI, _, _, _, err := media.NewTransferer(t.main.Config.AnimatedSticker).
WithIsSticker(true).
Transfer(ctx, t.main.Store, t.client.API(), t.main.Bridge.Bot, &tg.InputDocumentFileLocation{
ID: document.GetID(),
AccessHash: document.GetAccessHash(),
FileReference: document.GetFileReference(),
})
if err != nil {
return nil, err
}
result[ids.MakeEmojiIDFromDocumentID(document.ID)] = string(mxcURI)
}
}
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 {
// TODO deal with the custom emoji IDs
customEmojis, err := t.transferEmojisToMatrix(ctx, customEmojiIDs)
if err != nil {
return err
@@ -359,9 +370,8 @@ func (t *TelegramClient) handleTelegramParsedReactionsLocked(ctx context.Context
if onlyUserID != nil && existing.SenderID != *onlyUserID {
continue
}
newReactions := reactions[existing.SenderID]
var matched bool
reactions[existing.SenderID], matched, err = reactionsFilter(newReactions, existing)
reactions[existing.SenderID], matched, err = reactionsFilter(reactions[existing.SenderID], existing)
if err != nil {
return err
} else if !matched {
@@ -369,7 +379,7 @@ func (t *TelegramClient) handleTelegramParsedReactionsLocked(ctx context.Context
removed = append(removed, existing)
} else if reactionLimit, err := t.getReactionLimit(ctx, existing.SenderID); err != nil {
return err
} else if len(newReactions) == reactionLimit {
} else if len(reactions[existing.SenderID]) >= reactionLimit {
removed = append(removed, existing)
}
}
@@ -447,7 +457,6 @@ func (t *TelegramClient) handleTelegramParsedReactionsLocked(ctx context.Context
t.main.Bridge.QueueRemoteEvent(t.userLogin, evt)
}
fmt.Printf("%v %v %v\n", isFull, reactions, customEmojiIDs)
return nil
}