commands/imagepack: include pack metadata in sticker info
This commit is contained in:
@@ -42,7 +42,7 @@ require (
|
|||||||
golang.org/x/sync v0.20.0
|
golang.org/x/sync v0.20.0
|
||||||
golang.org/x/tools v0.44.0
|
golang.org/x/tools v0.44.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
maunium.net/go/mautrix v0.27.1-0.20260420202037-7783de22f56c
|
maunium.net/go/mautrix v0.27.1-0.20260422171355-c6fe96e2dea3
|
||||||
rsc.io/qr v0.2.0
|
rsc.io/qr v0.2.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -236,7 +236,7 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
maunium.net/go/mauflag v1.0.0 h1:YiaRc0tEI3toYtJMRIfjP+jklH45uDHtT80nUamyD4M=
|
maunium.net/go/mauflag v1.0.0 h1:YiaRc0tEI3toYtJMRIfjP+jklH45uDHtT80nUamyD4M=
|
||||||
maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA=
|
maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA=
|
||||||
maunium.net/go/mautrix v0.27.1-0.20260420202037-7783de22f56c h1:qHUNGpgxCoEwK+d+Hqep9vRf+ds9lzOaVy245/Q3LuU=
|
maunium.net/go/mautrix v0.27.1-0.20260422171355-c6fe96e2dea3 h1:V5L7Yo0fH1fs6lybfR+BUWG1D25xIdUZNWBIPXCV8cY=
|
||||||
maunium.net/go/mautrix v0.27.1-0.20260420202037-7783de22f56c/go.mod h1:7QpEQiTy6p4LHkXXaZI+N46tGYy8HMhD0JjzZAFoFWs=
|
maunium.net/go/mautrix v0.27.1-0.20260422171355-c6fe96e2dea3/go.mod h1:7QpEQiTy6p4LHkXXaZI+N46tGYy8HMhD0JjzZAFoFWs=
|
||||||
rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY=
|
rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY=
|
||||||
rsc.io/qr v0.2.0/go.mod h1:IF+uZjkb9fqyeF/4tlBoynqmQxUoPfWEKh921coOuXs=
|
rsc.io/qr v0.2.0/go.mod h1:IF+uZjkb9fqyeF/4tlBoynqmQxUoPfWEKh921coOuXs=
|
||||||
|
|||||||
@@ -117,6 +117,9 @@ type TelegramClient struct {
|
|||||||
|
|
||||||
prevReactionPoll map[networkid.PortalKey]time.Time
|
prevReactionPoll map[networkid.PortalKey]time.Time
|
||||||
prevReactionPollLock sync.Mutex
|
prevReactionPollLock sync.Mutex
|
||||||
|
|
||||||
|
stickerPackCache map[string]map[int64]*tg.Document
|
||||||
|
stickerPackCacheLock sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ bridgev2.NetworkAPI = (*TelegramClient)(nil)
|
var _ bridgev2.NetworkAPI = (*TelegramClient)(nil)
|
||||||
@@ -172,6 +175,7 @@ func NewTelegramClient(ctx context.Context, tc *TelegramConnector, login *bridge
|
|||||||
takeoutAccepted: exsync.NewEvent(),
|
takeoutAccepted: exsync.NewEvent(),
|
||||||
|
|
||||||
prevReactionPoll: map[networkid.PortalKey]time.Time{},
|
prevReactionPoll: map[networkid.PortalKey]time.Time{},
|
||||||
|
stickerPackCache: map[string]map[int64]*tg.Document{},
|
||||||
|
|
||||||
recentMessageRooms: exsync.NewRingBuffer[networkid.MessageID, networkid.PortalKey](32),
|
recentMessageRooms: exsync.NewRingBuffer[networkid.MessageID, networkid.PortalKey](32),
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ import (
|
|||||||
"go.mau.fi/mautrix-telegram/pkg/gotd/telegram/message"
|
"go.mau.fi/mautrix-telegram/pkg/gotd/telegram/message"
|
||||||
"go.mau.fi/mautrix-telegram/pkg/gotd/telegram/uploader"
|
"go.mau.fi/mautrix-telegram/pkg/gotd/telegram/uploader"
|
||||||
"go.mau.fi/mautrix-telegram/pkg/gotd/tg"
|
"go.mau.fi/mautrix-telegram/pkg/gotd/tg"
|
||||||
|
"go.mau.fi/mautrix-telegram/pkg/gotd/tgerr"
|
||||||
|
|
||||||
"go.mau.fi/mautrix-telegram/pkg/connector/emojis"
|
"go.mau.fi/mautrix-telegram/pkg/connector/emojis"
|
||||||
"go.mau.fi/mautrix-telegram/pkg/connector/humanise"
|
"go.mau.fi/mautrix-telegram/pkg/connector/humanise"
|
||||||
@@ -242,10 +243,90 @@ func (tc *TelegramClient) pollSponsoredMessage(ctx context.Context, portal *brid
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tc *TelegramClient) transferMediaToTelegram(ctx context.Context, content *event.MessageEventContent, sticker, forceDocument bool) (tg.InputMediaClass, error) {
|
func (tc *TelegramClient) parseInputPack(meta map[string]any) (shortName string, id, accessHash int64) {
|
||||||
|
shortName, _ = meta["short_name"].(string)
|
||||||
|
idStr, _ := meta["id"].(string)
|
||||||
|
if idStr != "" {
|
||||||
|
id, _ = strconv.ParseInt(idStr, 10, 64)
|
||||||
|
}
|
||||||
|
accessHashStr, _ := meta["access_hash"].(string)
|
||||||
|
accessHashSourceStr, _ := meta["access_hash_source"].(string)
|
||||||
|
if id != 0 && accessHashStr != "" && accessHashSourceStr != "" {
|
||||||
|
accessHashSource, _ := strconv.ParseInt(accessHashSourceStr, 10, 64)
|
||||||
|
if accessHashSource == tc.telegramUserID {
|
||||||
|
accessHash, _ = strconv.ParseInt(accessHashStr, 10, 64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc *TelegramClient) findOriginalStickerDocument(ctx context.Context, info map[string]any, forceClearCache bool) (tg.InputMediaClass, error) {
|
||||||
|
stickerIDStr, ok := info["id"].(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
stickerID, err := strconv.ParseInt(stickerIDStr, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
pack, ok := info["pack"].(map[string]any)
|
||||||
|
if !ok {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
var inputPack tg.InputStickerSetClass
|
||||||
|
var cacheKey string
|
||||||
|
packName, packID, packAccessHash := tc.parseInputPack(pack)
|
||||||
|
if packAccessHash != 0 {
|
||||||
|
inputPack = &tg.InputStickerSetID{ID: packID, AccessHash: packAccessHash}
|
||||||
|
cacheKey = fmt.Sprintf("pack_id:%d", packID)
|
||||||
|
} else if packName != "" {
|
||||||
|
inputPack = &tg.InputStickerSetShortName{ShortName: packName}
|
||||||
|
cacheKey = fmt.Sprintf("pack_name:%s", packName)
|
||||||
|
} else {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
tc.stickerPackCacheLock.Lock()
|
||||||
|
defer tc.stickerPackCacheLock.Unlock()
|
||||||
|
docMap, ok := tc.stickerPackCache[cacheKey]
|
||||||
|
if !ok || forceClearCache {
|
||||||
|
resp, err := tc.client.API().MessagesGetStickerSet(ctx, &tg.MessagesGetStickerSetRequest{Stickerset: inputPack})
|
||||||
|
if err != nil {
|
||||||
|
if tgerr.Is(err, tg.ErrStickersetInvalid) {
|
||||||
|
tc.stickerPackCache[cacheKey] = nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("failed to get sticker set: %w", err)
|
||||||
|
}
|
||||||
|
set, ok := resp.AsModified()
|
||||||
|
if !ok {
|
||||||
|
tc.stickerPackCache[cacheKey] = nil
|
||||||
|
return nil, fmt.Errorf("unexpected response type for MessagesGetStickerSet: %T", resp)
|
||||||
|
}
|
||||||
|
docMap = set.MapDocuments().DocumentToMap()
|
||||||
|
tc.stickerPackCache[cacheKey] = docMap
|
||||||
|
tc.stickerPackCache[fmt.Sprintf("pack_id:%d", set.Set.ID)] = docMap
|
||||||
|
tc.stickerPackCache[fmt.Sprintf("pack_name:%s", set.Set.ShortName)] = docMap
|
||||||
|
}
|
||||||
|
stickerDoc, ok := docMap[stickerID]
|
||||||
|
if !ok {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return &tg.InputMediaDocument{ID: stickerDoc.AsInput()}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc *TelegramClient) transferMediaToTelegram(ctx context.Context, content *event.MessageEventContent, sticker, forceRetry, forceDocument bool) (tg.InputMediaClass, error) {
|
||||||
var upload tg.InputFileClass
|
var upload tg.InputFileClass
|
||||||
filename := getMediaFilename(content)
|
filename := getMediaFilename(content)
|
||||||
info := content.GetInfo()
|
info := content.GetInfo()
|
||||||
|
if sticker {
|
||||||
|
extra, ok := info.Extra["fi.mau.telegram.sticker"].(map[string]any)
|
||||||
|
if !ok {
|
||||||
|
// Not from telegram, continue to reupload
|
||||||
|
} else if origFile, err := tc.findOriginalStickerDocument(ctx, extra, forceRetry); err != nil {
|
||||||
|
zerolog.Ctx(ctx).Err(err).Msg("Failed to find original sticker document, falling back to reupload")
|
||||||
|
} else if origFile != nil {
|
||||||
|
return origFile, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
err := tc.main.Bridge.Bot.DownloadMediaToFile(ctx, content.URL, content.File, false, func(f *os.File) (err error) {
|
err := tc.main.Bridge.Bot.DownloadMediaToFile(ctx, content.URL, content.File, false, func(f *os.File) (err error) {
|
||||||
uploadFilename := f.Name()
|
uploadFilename := f.Name()
|
||||||
if sticker && (info.MimeType == "image/png" || info.MimeType == "image/jpeg") {
|
if sticker && (info.MimeType == "image/png" || info.MimeType == "image/jpeg") {
|
||||||
@@ -458,19 +539,26 @@ func (tc *TelegramClient) HandleMatrixMessage(ctx context.Context, msg *bridgev2
|
|||||||
|
|
||||||
var updates tg.UpdatesClass
|
var updates tg.UpdatesClass
|
||||||
if msg.Event.Type == event.EventSticker {
|
if msg.Event.Type == event.EventSticker {
|
||||||
var media tg.InputMediaClass
|
mediaReq := &tg.MessagesSendMediaRequest{
|
||||||
media, err = tc.transferMediaToTelegram(ctx, msg.Content, true, false)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
updates, err = tc.client.API().MessagesSendMedia(ctx, &tg.MessagesSendMediaRequest{
|
|
||||||
Peer: peer,
|
Peer: peer,
|
||||||
Message: message,
|
Message: message,
|
||||||
Entities: entities,
|
Entities: entities,
|
||||||
Media: media,
|
|
||||||
ReplyTo: replyTo,
|
ReplyTo: replyTo,
|
||||||
RandomID: randomID,
|
RandomID: randomID,
|
||||||
})
|
}
|
||||||
|
mediaReq.Media, err = tc.transferMediaToTelegram(ctx, msg.Content, true, false, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
updates, err = tc.client.API().MessagesSendMedia(ctx, mediaReq)
|
||||||
|
if tgerr.Is(err, tg.ErrFileReferenceExpired) {
|
||||||
|
zerolog.Ctx(ctx).Debug().AnErr("send_error", err).Msg("Trying to refetch sticker pack")
|
||||||
|
mediaReq.Media, err = tc.transferMediaToTelegram(ctx, msg.Content, true, true, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
updates, err = tc.client.API().MessagesSendMedia(ctx, mediaReq)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
switch msg.Content.MsgType {
|
switch msg.Content.MsgType {
|
||||||
case event.MsgText, event.MsgNotice, event.MsgEmote:
|
case event.MsgText, event.MsgNotice, event.MsgEmote:
|
||||||
@@ -485,7 +573,7 @@ func (tc *TelegramClient) HandleMatrixMessage(ctx context.Context, msg *bridgev2
|
|||||||
case event.MsgImage, event.MsgFile, event.MsgAudio, event.MsgVideo:
|
case event.MsgImage, event.MsgFile, event.MsgAudio, event.MsgVideo:
|
||||||
var media tg.InputMediaClass
|
var media tg.InputMediaClass
|
||||||
forceDocument, _ := msg.Event.Content.Raw["fi.mau.telegram.force_document"].(bool)
|
forceDocument, _ := msg.Event.Content.Raw["fi.mau.telegram.force_document"].(bool)
|
||||||
media, err = tc.transferMediaToTelegram(ctx, msg.Content, false, forceDocument)
|
media, err = tc.transferMediaToTelegram(ctx, msg.Content, false, false, forceDocument)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -650,7 +738,7 @@ func (tc *TelegramClient) HandleMatrixEdit(ctx context.Context, msg *bridgev2.Ma
|
|||||||
} else {
|
} else {
|
||||||
log.Info().Msg("media URI changed, re-uploading media")
|
log.Info().Msg("media URI changed, re-uploading media")
|
||||||
forceDocument, _ := msg.Event.Content.Raw["fi.mau.telegram.force_document"].(bool)
|
forceDocument, _ := msg.Event.Content.Raw["fi.mau.telegram.force_document"].(bool)
|
||||||
req.Media, err = tc.transferMediaToTelegram(ctx, msg.Content, false, forceDocument)
|
req.Media, err = tc.transferMediaToTelegram(ctx, msg.Content, false, false, forceDocument)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -482,6 +482,13 @@ func (tc *TelegramClient) fnDownloadEmojiPack(ce *commands.Event) {
|
|||||||
Attribution: fmt.Sprintf("Imported from https://t.me/%s/%s", linkType, set.Set.ShortName),
|
Attribution: fmt.Sprintf("Imported from https://t.me/%s/%s", linkType, set.Set.ShortName),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
topLevelExtra := map[string]any{
|
||||||
|
"fi.mau.telegram.stickerpack": map[string]any{
|
||||||
|
"id": strconv.FormatInt(set.Set.ID, 10),
|
||||||
|
"short_name": set.Set.ShortName,
|
||||||
|
"emoji_pack": set.Set.Emojis,
|
||||||
|
},
|
||||||
|
}
|
||||||
keywords := make(map[int64][]string)
|
keywords := make(map[int64][]string)
|
||||||
emojis := make(map[int64][]string)
|
emojis := make(map[int64][]string)
|
||||||
for _, kw := range set.Keywords {
|
for _, kw := range set.Keywords {
|
||||||
@@ -524,13 +531,29 @@ func (tc *TelegramClient) fnDownloadEmojiPack(ce *commands.Event) {
|
|||||||
if len(imageEmojis) > 0 {
|
if len(imageEmojis) > 0 {
|
||||||
body = imageEmojis[0]
|
body = imageEmojis[0]
|
||||||
}
|
}
|
||||||
|
if !set.Set.Emojis {
|
||||||
|
// Stickers need extra info in each sticker so they can be accurately bridged back to Telegram
|
||||||
|
// Custom emojis don't have space for such info and can be used with just the document ID
|
||||||
|
info.Extra = map[string]any{
|
||||||
|
"fi.mau.telegram.sticker": map[string]any{
|
||||||
|
"id": strconv.FormatInt(rawDoc.GetID(), 10),
|
||||||
|
"pack": map[string]any{
|
||||||
|
"short_name": set.Set.ShortName,
|
||||||
|
"id": strconv.FormatInt(set.Set.ID, 10),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
pack.Images[key] = &event.ImagePackImage{
|
pack.Images[key] = &event.ImagePackImage{
|
||||||
URL: mxc,
|
URL: mxc,
|
||||||
Body: body,
|
Body: body,
|
||||||
Info: info,
|
Info: info,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_, err = tc.main.Bridge.Bot.SendState(ce.Ctx, spaceRoom, event.StateUnstableImagePack, set.Set.ShortName, &event.Content{Parsed: pack}, time.Now())
|
_, err = tc.main.Bridge.Bot.SendState(ce.Ctx, spaceRoom, event.StateUnstableImagePack, set.Set.ShortName, &event.Content{
|
||||||
|
Parsed: pack,
|
||||||
|
Raw: topLevelExtra,
|
||||||
|
}, time.Now())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ce.Reply("Failed to send image pack to space: %v", err)
|
ce.Reply("Failed to send image pack to space: %v", err)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -606,7 +606,7 @@ func (tc *TelegramClient) convertMediaRequiringUpload(
|
|||||||
}
|
}
|
||||||
case *tg.DocumentAttributeImageSize:
|
case *tg.DocumentAttributeImageSize:
|
||||||
transferer = transferer.WithImageSize(a)
|
transferer = transferer.WithImageSize(a)
|
||||||
if content.MsgType == event.MsgFile {
|
if content.MsgType == event.MsgFile && !isSticker {
|
||||||
content.MsgType = event.MsgImage
|
content.MsgType = event.MsgImage
|
||||||
extra["fi.mau.telegram.force_document"] = true
|
extra["fi.mau.telegram.force_document"] = true
|
||||||
defaultFileName = "image_document"
|
defaultFileName = "image_document"
|
||||||
@@ -623,8 +623,9 @@ func (tc *TelegramClient) convertMediaRequiringUpload(
|
|||||||
|
|
||||||
if setID, ok := a.Stickerset.(*tg.InputStickerSetID); ok {
|
if setID, ok := a.Stickerset.(*tg.InputStickerSetID); ok {
|
||||||
stickerInfo["pack"] = map[string]any{
|
stickerInfo["pack"] = map[string]any{
|
||||||
"id": strconv.FormatInt(setID.ID, 10),
|
"id": strconv.FormatInt(setID.ID, 10),
|
||||||
"access_hash": strconv.FormatInt(setID.AccessHash, 10),
|
"access_hash": strconv.FormatInt(setID.AccessHash, 10),
|
||||||
|
"access_hash_source": strconv.FormatInt(tc.telegramUserID, 10),
|
||||||
}
|
}
|
||||||
} else if shortName, ok := a.Stickerset.(*tg.InputStickerSetShortName); ok {
|
} else if shortName, ok := a.Stickerset.(*tg.InputStickerSetShortName); ok {
|
||||||
stickerInfo["pack"] = map[string]any{
|
stickerInfo["pack"] = map[string]any{
|
||||||
|
|||||||
Reference in New Issue
Block a user