mirror of
https://dev.narayana.im/narayana/telegabber.git
synced 2026-10-07 16:51:47 +00:00
157 lines
5.3 KiB
Go
157 lines
5.3 KiB
Go
package xmpp
|
|
|
|
import (
|
|
"strconv"
|
|
"strings"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
"gosrc.io/xmpp"
|
|
"gosrc.io/xmpp/stanza"
|
|
|
|
"dev.narayana.im/narayana/telegabber/telegram"
|
|
"dev.narayana.im/narayana/telegabber/xmpp/extensions"
|
|
"dev.narayana.im/narayana/telegabber/xmpp/gateway"
|
|
)
|
|
|
|
// collectForwardedMessages returns every XEP-0297 <forwarded/> wrapping a
|
|
// message on msg, in stanza order. A client forwarding several selected
|
|
// messages at once (e.g. an album plus a reply) puts them all in one
|
|
// stanza as sibling <forwarded/> elements - stanza.Message.Get only
|
|
// returns the first match of a given extension type, so callers that
|
|
// need all of them must scan msg.Extensions directly instead.
|
|
func collectForwardedMessages(msg stanza.Message) []*extensions.ForwardedMessage {
|
|
var result []*extensions.ForwardedMessage
|
|
for _, e := range msg.Extensions {
|
|
if forwarded, ok := e.(*extensions.ForwardedMessage); ok && forwarded.Message != nil {
|
|
result = append(result, forwarded)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
// hasForwardedPayload reports whether msg carries at least one XEP-0297
|
|
// <forwarded/> wrapping a message - such stanzas may ride without a plain
|
|
// body, mirroring hasReactionsPayload/hasOMEMOPayload's role in
|
|
// HandleMessage's body-gate.
|
|
func hasForwardedPayload(msg stanza.Message) bool {
|
|
return len(collectForwardedMessages(msg)) > 0
|
|
}
|
|
|
|
// fileSharingNames extracts each XEP-0447 <file-sharing><file><name>
|
|
// value nested in inner - a client bundling several files (e.g. a
|
|
// gallery selection) under one forwarded reference lists them as sibling
|
|
// file-sharing elements rather than separate <forwarded/> wrappers.
|
|
func fileSharingNames(inner *extensions.ClientMessage) []string {
|
|
var names []string
|
|
for _, e := range inner.Extensions {
|
|
if fs, ok := e.(*extensions.FileSharing); ok && fs.File.Name != "" {
|
|
names = append(names, fs.File.Name)
|
|
}
|
|
}
|
|
return names
|
|
}
|
|
|
|
// handleIncomingForward relays one or more native XEP-0297 forwards to
|
|
// Telegram via TDLib's own ForwardMessages, ignoring whatever plain-text
|
|
// body/fallback rides alongside them - only each wrapped message's own
|
|
// identity matters. Message ids are grouped by their resolved source
|
|
// chat and forwarded together per chat (in original order) so TDLib can
|
|
// still recognize and re-form an album, rather than resending each item
|
|
// as its own standalone forward. bare and toID are already resolved by
|
|
// HandleMessage's caller; toID is the forward's destination chat.
|
|
func handleIncomingForward(component *xmpp.Component, msg stanza.Message, session *telegram.Client, bare string, toID int64, isGroupchat bool, forwardedList []*extensions.ForwardedMessage) {
|
|
type sourceGroup struct {
|
|
chatId int64
|
|
msgIds []int64
|
|
seen map[int64]bool
|
|
}
|
|
var groups []*sourceGroup
|
|
groupByChatId := make(map[int64]*sourceGroup)
|
|
|
|
addToGroup := func(chatId int64, msgIds []int64) {
|
|
group, ok := groupByChatId[chatId]
|
|
if !ok {
|
|
group = &sourceGroup{chatId: chatId, seen: make(map[int64]bool)}
|
|
groupByChatId[chatId] = group
|
|
groups = append(groups, group)
|
|
}
|
|
for _, msgId := range msgIds {
|
|
if group.seen[msgId] {
|
|
continue
|
|
}
|
|
group.seen[msgId] = true
|
|
group.msgIds = append(group.msgIds, msgId)
|
|
}
|
|
}
|
|
|
|
for _, forwarded := range forwardedList {
|
|
inner := forwarded.Message
|
|
if inner.Id == "" {
|
|
log.Warn("Forwarded message has no id, cannot resolve its source")
|
|
continue
|
|
}
|
|
|
|
var sourceChatId, sourceMsgId int64
|
|
if chatId, msgId, err := gateway.IdsDB.GetByXmppId(session.Session.Login, bare, inner.Id); err == nil {
|
|
sourceChatId, sourceMsgId = chatId, msgId
|
|
} else {
|
|
msgId, ok := parseMessageId(inner.Id)
|
|
if !ok {
|
|
continue
|
|
}
|
|
fromBare, _, ok := gateway.SplitJID(inner.From)
|
|
if !ok {
|
|
log.Warn("Could not parse forwarded message sender JID")
|
|
continue
|
|
}
|
|
node := strings.TrimPrefix(strings.SplitN(fromBare, "@", 2)[0], "c")
|
|
chatId, parseErr := strconv.ParseInt(node, 10, 64)
|
|
if parseErr != nil {
|
|
log.Warn("Could not parse forwarded source chat id")
|
|
continue
|
|
}
|
|
sourceChatId, sourceMsgId = chatId, msgId
|
|
}
|
|
|
|
// A reference bundling several files (SendAlbumToGateway's own
|
|
// anchor id, or any client that groups a gallery selection this
|
|
// way) only ever names one representative message on its own -
|
|
// match the embedded file-sharing filenames back to the album's
|
|
// real per-file messages instead of resending just that one.
|
|
if names := fileSharingNames(inner); len(names) > 1 {
|
|
matched := session.ResolveAlbumFileNames(sourceChatId, sourceMsgId, names)
|
|
log.Infof("Forwarded album %v/%v: names=%v matched=%v", sourceChatId, sourceMsgId, names, matched)
|
|
if len(matched) < len(names) {
|
|
log.Warnf("Only matched %v/%v forwarded file-sharing names to album messages for %v/%v", len(matched), len(names), sourceChatId, sourceMsgId)
|
|
}
|
|
if len(matched) > 0 {
|
|
addToGroup(sourceChatId, matched)
|
|
continue
|
|
}
|
|
}
|
|
|
|
addToGroup(sourceChatId, []int64{sourceMsgId})
|
|
}
|
|
|
|
if len(groups) == 0 {
|
|
return
|
|
}
|
|
|
|
var from string
|
|
if isGroupchat {
|
|
from = gateway.MUCJID(toID)
|
|
} else {
|
|
from = gateway.CHATNODE(toID)
|
|
}
|
|
|
|
for _, group := range groups {
|
|
messages, err := session.ForwardMessage(group.chatId, toID, group.msgIds)
|
|
if err != nil {
|
|
gateway.SendErrorMessage(msg.From, from, err.Error(), 500, isGroupchat, component)
|
|
continue
|
|
}
|
|
for _, message := range messages {
|
|
session.ProcessIncomingMessage(toID, message)
|
|
}
|
|
}
|
|
}
|