2024-09-25 16:01:35 +03:00
|
|
|
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
|
|
|
|
|
// Copyright (C) 2024 Tulir Asokan
|
|
|
|
|
//
|
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
// (at your option) any later version.
|
|
|
|
|
//
|
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
// GNU Affero General Public License for more details.
|
|
|
|
|
//
|
|
|
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
|
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
|
|
2024-08-13 14:11:10 +03:00
|
|
|
package msgconv
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"fmt"
|
2024-09-06 17:41:26 +03:00
|
|
|
"html"
|
2024-08-13 14:11:10 +03:00
|
|
|
_ "image/gif"
|
|
|
|
|
_ "image/jpeg"
|
|
|
|
|
_ "image/png"
|
|
|
|
|
"strings"
|
2024-09-25 16:01:35 +03:00
|
|
|
"time"
|
2024-08-13 14:11:10 +03:00
|
|
|
|
|
|
|
|
"github.com/rs/zerolog"
|
2025-04-08 21:00:38 +03:00
|
|
|
"go.mau.fi/util/ptr"
|
2024-08-13 14:11:10 +03:00
|
|
|
"go.mau.fi/whatsmeow"
|
2024-08-14 20:31:49 +03:00
|
|
|
"go.mau.fi/whatsmeow/proto/waE2E"
|
2024-08-13 14:11:10 +03:00
|
|
|
"go.mau.fi/whatsmeow/types"
|
|
|
|
|
_ "golang.org/x/image/webp"
|
|
|
|
|
"maunium.net/go/mautrix/bridgev2"
|
|
|
|
|
"maunium.net/go/mautrix/bridgev2/networkid"
|
|
|
|
|
"maunium.net/go/mautrix/event"
|
|
|
|
|
"maunium.net/go/mautrix/id"
|
|
|
|
|
|
2024-10-15 17:59:57 +03:00
|
|
|
"go.mau.fi/mautrix-whatsapp/pkg/waid"
|
2024-08-13 14:11:10 +03:00
|
|
|
)
|
|
|
|
|
|
2024-09-25 16:01:35 +03:00
|
|
|
type contextKey int
|
2024-08-14 20:31:49 +03:00
|
|
|
|
2024-09-25 16:01:35 +03:00
|
|
|
const (
|
|
|
|
|
contextKeyClient contextKey = iota
|
|
|
|
|
contextKeyIntent
|
|
|
|
|
contextKeyPortal
|
2025-07-29 17:10:25 +02:00
|
|
|
ContextKeyEditTargetID
|
2024-09-25 16:01:35 +03:00
|
|
|
)
|
2024-08-13 14:11:10 +03:00
|
|
|
|
2024-09-25 16:01:35 +03:00
|
|
|
func getClient(ctx context.Context) *whatsmeow.Client {
|
|
|
|
|
return ctx.Value(contextKeyClient).(*whatsmeow.Client)
|
2024-08-14 20:31:49 +03:00
|
|
|
}
|
|
|
|
|
|
2024-09-25 16:01:35 +03:00
|
|
|
func getIntent(ctx context.Context) bridgev2.MatrixAPI {
|
|
|
|
|
return ctx.Value(contextKeyIntent).(bridgev2.MatrixAPI)
|
|
|
|
|
}
|
2024-09-04 05:46:27 +03:00
|
|
|
|
2024-09-25 16:01:35 +03:00
|
|
|
func getPortal(ctx context.Context) *bridgev2.Portal {
|
|
|
|
|
return ctx.Value(contextKeyPortal).(*bridgev2.Portal)
|
2024-09-04 05:46:27 +03:00
|
|
|
}
|
|
|
|
|
|
2025-07-29 17:10:25 +02:00
|
|
|
func getEditTargetID(ctx context.Context) types.MessageID {
|
|
|
|
|
editID, _ := ctx.Value(ContextKeyEditTargetID).(types.MessageID)
|
|
|
|
|
return editID
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-30 17:15:36 +03:00
|
|
|
func (mc *MessageConverter) getBasicUserInfo(ctx context.Context, user types.JID) (id.UserID, string, error) {
|
|
|
|
|
ghost, err := mc.Bridge.GetGhostByID(ctx, waid.MakeUserID(user))
|
2024-09-06 17:41:26 +03:00
|
|
|
if err != nil {
|
|
|
|
|
return "", "", fmt.Errorf("failed to get ghost by ID: %w", err)
|
|
|
|
|
}
|
2025-05-30 17:15:36 +03:00
|
|
|
var pnJID types.JID
|
|
|
|
|
if user.Server == types.DefaultUserServer {
|
|
|
|
|
pnJID = user
|
|
|
|
|
} else if user.Server == types.HiddenUserServer {
|
|
|
|
|
cli := getClient(ctx)
|
|
|
|
|
if user.User == cli.Store.GetLID().User {
|
|
|
|
|
pnJID = cli.Store.GetJID()
|
|
|
|
|
} else {
|
|
|
|
|
pnJID, err = cli.Store.LIDs.GetPNForLID(ctx, user)
|
|
|
|
|
if err != nil {
|
|
|
|
|
zerolog.Ctx(ctx).Err(err).
|
|
|
|
|
Stringer("lid", user).
|
|
|
|
|
Msg("Failed to get PN for LID in mention bridging")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if !pnJID.IsEmpty() {
|
2025-08-21 17:03:30 +03:00
|
|
|
portal := getPortal(ctx)
|
2025-05-30 17:15:36 +03:00
|
|
|
login := mc.Bridge.GetCachedUserLoginByID(waid.MakeUserLoginID(pnJID))
|
2025-08-21 17:03:30 +03:00
|
|
|
if login != nil && (portal.Receiver == "" || portal.Receiver == login.ID) {
|
2025-05-30 17:15:36 +03:00
|
|
|
return login.UserMXID, ghost.Name, nil
|
|
|
|
|
}
|
2024-09-06 17:41:26 +03:00
|
|
|
}
|
|
|
|
|
return ghost.Intent.GetMXID(), ghost.Name, nil
|
|
|
|
|
}
|
2024-08-14 20:31:49 +03:00
|
|
|
|
2024-09-06 17:41:26 +03:00
|
|
|
func (mc *MessageConverter) addMentions(ctx context.Context, mentionedJID []string, into *event.MessageEventContent) {
|
|
|
|
|
if len(mentionedJID) == 0 {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
into.EnsureHasHTML()
|
|
|
|
|
for _, jid := range mentionedJID {
|
|
|
|
|
parsed, err := types.ParseJID(jid)
|
|
|
|
|
if err != nil {
|
|
|
|
|
zerolog.Ctx(ctx).Err(err).Str("jid", jid).Msg("Failed to parse mentioned JID")
|
|
|
|
|
continue
|
2024-08-14 20:31:49 +03:00
|
|
|
}
|
2025-05-30 17:15:36 +03:00
|
|
|
mxid, displayname, err := mc.getBasicUserInfo(ctx, parsed)
|
2024-09-06 17:41:26 +03:00
|
|
|
if err != nil {
|
|
|
|
|
zerolog.Ctx(ctx).Err(err).Str("jid", jid).Msg("Failed to get user info")
|
|
|
|
|
continue
|
2024-08-13 14:11:10 +03:00
|
|
|
}
|
2024-09-06 17:41:26 +03:00
|
|
|
into.Mentions.UserIDs = append(into.Mentions.UserIDs, mxid)
|
|
|
|
|
mentionText := "@" + parsed.User
|
|
|
|
|
into.Body = strings.ReplaceAll(into.Body, mentionText, displayname)
|
|
|
|
|
into.FormattedBody = strings.ReplaceAll(into.FormattedBody, mentionText, fmt.Sprintf(`<a href="%s">%s</a>`, mxid.URI().MatrixToURL(), html.EscapeString(displayname)))
|
2024-08-13 14:11:10 +03:00
|
|
|
}
|
2024-08-14 20:31:49 +03:00
|
|
|
}
|
2024-08-13 14:11:10 +03:00
|
|
|
|
2025-04-08 21:00:38 +03:00
|
|
|
var failedCommentPart = &bridgev2.ConvertedMessagePart{
|
|
|
|
|
Type: event.EventMessage,
|
|
|
|
|
Content: &event.MessageEventContent{
|
|
|
|
|
Body: "Failed to decrypt comment",
|
|
|
|
|
MsgType: event.MsgNotice,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-16 15:41:23 +03:00
|
|
|
func (mc *MessageConverter) ToMatrix(
|
|
|
|
|
ctx context.Context,
|
|
|
|
|
portal *bridgev2.Portal,
|
|
|
|
|
client *whatsmeow.Client,
|
|
|
|
|
intent bridgev2.MatrixAPI,
|
2024-09-25 16:01:35 +03:00
|
|
|
waMsg *waE2E.Message,
|
2025-08-06 16:37:23 +03:00
|
|
|
rawWaMsg *waE2E.Message,
|
2024-09-16 15:41:23 +03:00
|
|
|
info *types.MessageInfo,
|
2024-10-07 17:13:20 +03:00
|
|
|
isViewOnce bool,
|
2025-09-17 15:04:12 +03:00
|
|
|
isBackfill bool,
|
2024-10-14 19:52:16 +03:00
|
|
|
previouslyConvertedPart *bridgev2.ConvertedMessagePart,
|
2024-09-16 15:41:23 +03:00
|
|
|
) *bridgev2.ConvertedMessage {
|
2026-03-18 18:21:17 +02:00
|
|
|
if waMsg == nil {
|
|
|
|
|
waMsg = &waE2E.Message{}
|
|
|
|
|
}
|
2024-09-25 16:01:35 +03:00
|
|
|
ctx = context.WithValue(ctx, contextKeyClient, client)
|
|
|
|
|
ctx = context.WithValue(ctx, contextKeyIntent, intent)
|
|
|
|
|
ctx = context.WithValue(ctx, contextKeyPortal, portal)
|
2024-09-06 17:41:26 +03:00
|
|
|
var part *bridgev2.ConvertedMessagePart
|
|
|
|
|
var contextInfo *waE2E.ContextInfo
|
2024-09-25 16:01:35 +03:00
|
|
|
switch {
|
2024-09-25 16:39:49 +03:00
|
|
|
case waMsg.Conversation != nil, waMsg.ExtendedTextMessage != nil:
|
2024-09-25 16:01:35 +03:00
|
|
|
part, contextInfo = mc.convertTextMessage(ctx, waMsg)
|
|
|
|
|
case waMsg.TemplateMessage != nil:
|
|
|
|
|
part, contextInfo = mc.convertTemplateMessage(ctx, info, waMsg.TemplateMessage)
|
2025-03-17 22:32:00 +02:00
|
|
|
case waMsg.ButtonsMessage != nil:
|
|
|
|
|
part, contextInfo = mc.convertButtonsMessage(ctx, info, waMsg.ButtonsMessage)
|
|
|
|
|
case waMsg.ButtonsResponseMessage != nil:
|
|
|
|
|
part, contextInfo = mc.convertButtonsResponseMessage(ctx, waMsg.ButtonsResponseMessage)
|
2025-03-17 22:43:38 +02:00
|
|
|
case waMsg.InteractiveMessage != nil:
|
|
|
|
|
part, contextInfo = mc.convertInteractiveMessage(ctx, info, waMsg.InteractiveMessage)
|
|
|
|
|
case waMsg.InteractiveResponseMessage != nil:
|
|
|
|
|
part, contextInfo = mc.convertInteractiveResponseMessage(ctx, waMsg.InteractiveResponseMessage)
|
2024-09-25 16:01:35 +03:00
|
|
|
case waMsg.HighlyStructuredMessage != nil:
|
|
|
|
|
part, contextInfo = mc.convertTemplateMessage(ctx, info, waMsg.HighlyStructuredMessage.GetHydratedHsm())
|
|
|
|
|
case waMsg.TemplateButtonReplyMessage != nil:
|
|
|
|
|
part, contextInfo = mc.convertTemplateButtonReplyMessage(ctx, waMsg.TemplateButtonReplyMessage)
|
|
|
|
|
case waMsg.ListMessage != nil:
|
|
|
|
|
part, contextInfo = mc.convertListMessage(ctx, waMsg.ListMessage)
|
|
|
|
|
case waMsg.ListResponseMessage != nil:
|
|
|
|
|
part, contextInfo = mc.convertListResponseMessage(ctx, waMsg.ListResponseMessage)
|
|
|
|
|
case waMsg.PollCreationMessage != nil:
|
|
|
|
|
part, contextInfo = mc.convertPollCreationMessage(ctx, waMsg.PollCreationMessage)
|
|
|
|
|
case waMsg.PollCreationMessageV2 != nil:
|
|
|
|
|
part, contextInfo = mc.convertPollCreationMessage(ctx, waMsg.PollCreationMessageV2)
|
|
|
|
|
case waMsg.PollCreationMessageV3 != nil:
|
|
|
|
|
part, contextInfo = mc.convertPollCreationMessage(ctx, waMsg.PollCreationMessageV3)
|
|
|
|
|
case waMsg.PollUpdateMessage != nil:
|
|
|
|
|
part, contextInfo = mc.convertPollUpdateMessage(ctx, info, waMsg.PollUpdateMessage)
|
|
|
|
|
case waMsg.EventMessage != nil:
|
|
|
|
|
part, contextInfo = mc.convertEventMessage(ctx, waMsg.EventMessage)
|
2025-08-15 19:29:44 +05:30
|
|
|
case waMsg.PinInChatMessage != nil:
|
|
|
|
|
part, contextInfo = mc.convertPinInChatMessage(ctx, waMsg.PinInChatMessage)
|
2025-08-15 20:00:47 +05:30
|
|
|
case waMsg.KeepInChatMessage != nil:
|
|
|
|
|
part, contextInfo = mc.convertKeepInChatMessage(ctx, waMsg.KeepInChatMessage)
|
2025-08-15 20:29:57 +05:30
|
|
|
case waMsg.RichResponseMessage != nil:
|
|
|
|
|
part, contextInfo = mc.convertRichResponseMessage(ctx, waMsg.RichResponseMessage)
|
2024-09-25 16:01:35 +03:00
|
|
|
case waMsg.ImageMessage != nil:
|
2024-11-06 13:14:12 +01:00
|
|
|
part, contextInfo = mc.convertMediaMessage(ctx, waMsg.ImageMessage, "photo", info, isViewOnce, previouslyConvertedPart)
|
2024-09-25 16:01:35 +03:00
|
|
|
case waMsg.StickerMessage != nil:
|
2024-11-06 13:14:12 +01:00
|
|
|
part, contextInfo = mc.convertMediaMessage(ctx, waMsg.StickerMessage, "sticker", info, isViewOnce, previouslyConvertedPart)
|
2024-09-25 16:01:35 +03:00
|
|
|
case waMsg.VideoMessage != nil:
|
2024-11-06 13:14:12 +01:00
|
|
|
part, contextInfo = mc.convertMediaMessage(ctx, waMsg.VideoMessage, "video attachment", info, isViewOnce, previouslyConvertedPart)
|
2024-09-25 16:01:35 +03:00
|
|
|
case waMsg.PtvMessage != nil:
|
2024-11-06 13:14:12 +01:00
|
|
|
part, contextInfo = mc.convertMediaMessage(ctx, waMsg.PtvMessage, "video message", info, isViewOnce, previouslyConvertedPart)
|
2024-09-25 16:01:35 +03:00
|
|
|
case waMsg.AudioMessage != nil:
|
|
|
|
|
typeName := "audio attachment"
|
|
|
|
|
if waMsg.AudioMessage.GetPTT() {
|
|
|
|
|
typeName = "voice message"
|
|
|
|
|
}
|
2024-11-06 13:14:12 +01:00
|
|
|
part, contextInfo = mc.convertMediaMessage(ctx, waMsg.AudioMessage, typeName, info, isViewOnce, previouslyConvertedPart)
|
2024-09-25 16:01:35 +03:00
|
|
|
case waMsg.DocumentMessage != nil:
|
2024-11-06 13:14:12 +01:00
|
|
|
part, contextInfo = mc.convertMediaMessage(ctx, waMsg.DocumentMessage, "file attachment", info, isViewOnce, previouslyConvertedPart)
|
2025-07-17 15:29:41 +03:00
|
|
|
case waMsg.AlbumMessage != nil:
|
|
|
|
|
part, contextInfo = mc.convertAlbumMessage(ctx, waMsg.AlbumMessage)
|
2024-09-25 16:01:35 +03:00
|
|
|
case waMsg.LocationMessage != nil:
|
|
|
|
|
part, contextInfo = mc.convertLocationMessage(ctx, waMsg.LocationMessage)
|
|
|
|
|
case waMsg.LiveLocationMessage != nil:
|
|
|
|
|
part, contextInfo = mc.convertLiveLocationMessage(ctx, waMsg.LiveLocationMessage)
|
|
|
|
|
case waMsg.ContactMessage != nil:
|
|
|
|
|
part, contextInfo = mc.convertContactMessage(ctx, waMsg.ContactMessage)
|
|
|
|
|
case waMsg.ContactsArrayMessage != nil:
|
|
|
|
|
part, contextInfo = mc.convertContactsArrayMessage(ctx, waMsg.ContactsArrayMessage)
|
2024-10-14 19:56:40 +03:00
|
|
|
case waMsg.PlaceholderMessage != nil:
|
|
|
|
|
part, contextInfo = mc.convertPlaceholderMessage(ctx, waMsg)
|
2024-09-25 16:01:35 +03:00
|
|
|
case waMsg.GroupInviteMessage != nil:
|
|
|
|
|
part, contextInfo = mc.convertGroupInviteMessage(ctx, info, waMsg.GroupInviteMessage)
|
|
|
|
|
case waMsg.ProtocolMessage != nil && waMsg.ProtocolMessage.GetType() == waE2E.ProtocolMessage_EPHEMERAL_SETTING:
|
2025-09-17 15:04:12 +03:00
|
|
|
part, contextInfo = mc.convertEphemeralSettingMessage(ctx, waMsg.ProtocolMessage, info.Timestamp, isBackfill)
|
2026-03-26 22:02:48 -07:00
|
|
|
case waMsg.MessageHistoryBundle != nil:
|
|
|
|
|
part, contextInfo = mc.convertMessageHistoryShare(ctx, info, waMsg.MessageHistoryBundle.GetMessageHistoryMetadata(), waMsg.MessageHistoryBundle.GetContextInfo())
|
|
|
|
|
case waMsg.MessageHistoryNotice != nil:
|
|
|
|
|
part, contextInfo = mc.convertMessageHistoryShare(ctx, info, waMsg.MessageHistoryNotice.GetMessageHistoryMetadata(), waMsg.MessageHistoryNotice.GetContextInfo())
|
2025-04-08 21:00:38 +03:00
|
|
|
case waMsg.EncCommentMessage != nil:
|
|
|
|
|
part = failedCommentPart
|
2024-09-25 16:01:35 +03:00
|
|
|
default:
|
2025-08-06 16:37:23 +03:00
|
|
|
part, contextInfo = mc.convertUnknownMessage(ctx, rawWaMsg)
|
2024-08-13 14:11:10 +03:00
|
|
|
}
|
2024-08-14 20:31:49 +03:00
|
|
|
|
2024-09-06 17:41:26 +03:00
|
|
|
part.Content.Mentions = &event.Mentions{}
|
2024-09-25 16:01:35 +03:00
|
|
|
if part.DBMetadata == nil {
|
|
|
|
|
part.DBMetadata = &waid.MessageMetadata{}
|
2024-09-16 15:41:23 +03:00
|
|
|
}
|
2024-09-25 16:01:35 +03:00
|
|
|
dbMeta := part.DBMetadata.(*waid.MessageMetadata)
|
|
|
|
|
dbMeta.SenderDeviceID = info.Sender.Device
|
2024-09-16 15:41:23 +03:00
|
|
|
if info.IsIncomingBroadcast() {
|
2024-09-25 16:01:35 +03:00
|
|
|
dbMeta.BroadcastListJID = &info.Chat
|
2024-09-16 15:41:23 +03:00
|
|
|
if part.Extra == nil {
|
|
|
|
|
part.Extra = map[string]any{}
|
|
|
|
|
}
|
|
|
|
|
part.Extra["fi.mau.whatsapp.source_broadcast_list"] = info.Chat.String()
|
|
|
|
|
}
|
2024-09-06 17:41:26 +03:00
|
|
|
mc.addMentions(ctx, contextInfo.GetMentionedJID(), part.Content)
|
2026-03-18 15:26:28 +02:00
|
|
|
if contextInfo.GetNonJIDMentions() == 1 {
|
|
|
|
|
part.Content.Mentions.Room = true
|
|
|
|
|
}
|
2024-09-06 17:41:26 +03:00
|
|
|
|
|
|
|
|
cm := &bridgev2.ConvertedMessage{
|
2024-09-25 16:01:35 +03:00
|
|
|
Parts: []*bridgev2.ConvertedMessagePart{part},
|
|
|
|
|
}
|
|
|
|
|
if contextInfo.GetExpiration() > 0 {
|
|
|
|
|
cm.Disappear.Timer = time.Duration(contextInfo.GetExpiration()) * time.Second
|
2025-08-25 17:26:55 +05:30
|
|
|
cm.Disappear.Type = event.DisappearingTypeAfterSend
|
2025-07-03 21:18:13 +03:00
|
|
|
}
|
|
|
|
|
if portal.Disappear.Timer != cm.Disappear.Timer && portal.Metadata.(*waid.PortalMetadata).DisappearingTimerSetAt < contextInfo.GetEphemeralSettingTimestamp() {
|
2025-08-25 17:22:35 +03:00
|
|
|
portal.UpdateDisappearingSetting(ctx, cm.Disappear, bridgev2.UpdateDisappearingSettingOpts{
|
|
|
|
|
Sender: intent,
|
|
|
|
|
Timestamp: info.Timestamp,
|
|
|
|
|
Implicit: true,
|
|
|
|
|
Save: true,
|
|
|
|
|
SendNotice: true,
|
|
|
|
|
})
|
2024-09-06 17:41:26 +03:00
|
|
|
}
|
|
|
|
|
if contextInfo.GetStanzaID() != "" {
|
|
|
|
|
pcp, _ := types.ParseJID(contextInfo.GetParticipant())
|
|
|
|
|
chat, _ := types.ParseJID(contextInfo.GetRemoteJID())
|
|
|
|
|
if chat.IsEmpty() {
|
|
|
|
|
chat, _ = waid.ParsePortalID(portal.ID)
|
|
|
|
|
}
|
2025-11-09 11:27:39 +02:00
|
|
|
// We reroute all DMs to the phone number JID, so reroute reply participants too
|
2025-11-11 18:22:25 +02:00
|
|
|
pcp = rerouteMessageKey(ctx, chat, pcp, getPortal(ctx).Metadata.(*waid.PortalMetadata).AddressingMode == types.AddressingModeLID)
|
2025-11-09 13:35:51 +02:00
|
|
|
if store := getClient(ctx).Store; store != nil && chat.Server == types.DefaultUserServer && pcp.Server == types.HiddenUserServer {
|
2025-11-09 11:27:39 +02:00
|
|
|
pcpPN, _ := store.LIDs.GetPNForLID(ctx, pcp)
|
2025-11-09 13:35:51 +02:00
|
|
|
zerolog.Ctx(ctx).Debug().
|
|
|
|
|
Stringer("orig_participant", pcp).
|
|
|
|
|
Stringer("rerouted_participant", pcpPN).
|
|
|
|
|
Msg("Rerouting reply target (PN recipient in LID DM)")
|
2025-11-09 11:27:39 +02:00
|
|
|
if !pcpPN.IsEmpty() {
|
|
|
|
|
pcp = pcpPN
|
|
|
|
|
}
|
2025-11-09 13:35:51 +02:00
|
|
|
} else if store != nil && chat.Server == types.GroupServer && pcp.Server == types.DefaultUserServer && getPortal(ctx).Metadata.(*waid.PortalMetadata).AddressingMode == types.AddressingModeLID {
|
|
|
|
|
pcpLID, _ := store.LIDs.GetLIDForPN(ctx, pcp)
|
|
|
|
|
zerolog.Ctx(ctx).Debug().
|
|
|
|
|
Stringer("orig_participant", pcp).
|
|
|
|
|
Stringer("rerouted_participant", pcpLID).
|
|
|
|
|
Msg("Rerouting reply target (PN recipient in LID group)")
|
|
|
|
|
if !pcpLID.IsEmpty() {
|
|
|
|
|
pcp = pcpLID
|
|
|
|
|
}
|
2025-11-09 11:27:39 +02:00
|
|
|
}
|
2024-09-06 17:41:26 +03:00
|
|
|
cm.ReplyTo = &networkid.MessageOptionalPartID{
|
|
|
|
|
MessageID: waid.MakeMessageID(chat, pcp, contextInfo.GetStanzaID()),
|
|
|
|
|
}
|
2024-08-13 14:11:10 +03:00
|
|
|
}
|
2025-04-22 21:52:45 +03:00
|
|
|
if contextInfo.GetIsForwarded() {
|
|
|
|
|
hasCaption := part.Content.FileName != "" && part.Content.FileName != part.Content.Body
|
|
|
|
|
isMedia := part.Content.MsgType.IsMedia()
|
|
|
|
|
isText := part.Content.MsgType.IsText()
|
|
|
|
|
if isMedia && !hasCaption {
|
|
|
|
|
part.Content.FileName = part.Content.Body
|
|
|
|
|
part.Content.Body = "↷ Forwarded"
|
|
|
|
|
part.Content.Format = event.FormatHTML
|
|
|
|
|
part.Content.FormattedBody = "<p data-mx-forwarded-notice><em>↷ Forwarded</em></p>"
|
|
|
|
|
} else if isText || isMedia {
|
|
|
|
|
part.Content.EnsureHasHTML()
|
|
|
|
|
part.Content.Body = "↷ Forwarded\n\n" + part.Content.Body
|
|
|
|
|
part.Content.FormattedBody = "<p data-mx-forwarded-notice><em>↷ Forwarded</em></p>" + part.Content.FormattedBody
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-04-08 21:00:38 +03:00
|
|
|
commentTarget := waMsg.GetEncCommentMessage().GetTargetMessageKey()
|
|
|
|
|
if commentTarget == nil {
|
|
|
|
|
commentTarget = waMsg.GetCommentMessage().GetTargetMessageKey()
|
|
|
|
|
}
|
|
|
|
|
if commentTarget != nil {
|
|
|
|
|
pcp, _ := types.ParseJID(commentTarget.GetParticipant())
|
|
|
|
|
chat, _ := types.ParseJID(commentTarget.GetRemoteJID())
|
|
|
|
|
if chat.IsEmpty() {
|
|
|
|
|
chat, _ = waid.ParsePortalID(portal.ID)
|
|
|
|
|
}
|
|
|
|
|
cm.ThreadRoot = ptr.Ptr(waid.MakeMessageID(chat, pcp, commentTarget.GetID()))
|
|
|
|
|
}
|
2024-08-14 20:31:49 +03:00
|
|
|
|
2024-08-13 14:11:10 +03:00
|
|
|
return cm
|
|
|
|
|
}
|