From 04b8d27b5b0886a91cefb822e3fc13716d879676 Mon Sep 17 00:00:00 2001 From: Ping Chen Date: Sat, 13 Sep 2025 23:00:10 +0900 Subject: [PATCH 001/153] handlewhatsapp: bridge calendar event edit/cancellation --- pkg/connector/handlewhatsapp.go | 11 +++++++++++ pkg/msgconv/from-whatsapp.go | 3 +++ pkg/msgconv/wa-misc.go | 4 +++- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index e5e8029..777f6d9 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -340,6 +340,17 @@ func (wa *WhatsAppClient) handleWAMessage(ctx context.Context, evt *events.Messa evt.Message = decrypted } } + if encMessage := evt.Message.GetSecretEncryptedMessage(); encMessage != nil { + decrypted, err := wa.Client.DecryptSecretEncryptedMessage(ctx, evt) + if err != nil { + wa.UserLogin.Log.Err(err).Str("message_id", evt.Info.ID).Msg("Failed to decrypt message") + return + } + if decrypted.ProtocolMessage.GetType() == waE2E.ProtocolMessage_MESSAGE_EDIT { + parsedMessageType = "edit" + } + evt.Message = decrypted + } res := wa.UserLogin.QueueRemoteEvent(&WAMessageEvent{ MessageInfoWrapper: &MessageInfoWrapper{ Info: evt.Info, diff --git a/pkg/msgconv/from-whatsapp.go b/pkg/msgconv/from-whatsapp.go index 8e05384..a313efe 100644 --- a/pkg/msgconv/from-whatsapp.go +++ b/pkg/msgconv/from-whatsapp.go @@ -213,6 +213,9 @@ func (mc *MessageConverter) ToMatrix( part, contextInfo = mc.convertGroupInviteMessage(ctx, info, waMsg.GroupInviteMessage) case waMsg.ProtocolMessage != nil && waMsg.ProtocolMessage.GetType() == waE2E.ProtocolMessage_EPHEMERAL_SETTING: part, contextInfo = mc.convertEphemeralSettingMessage(ctx, waMsg.ProtocolMessage, info.Timestamp) + case waMsg.ProtocolMessage != nil && waMsg.ProtocolMessage.GetType() == waE2E.ProtocolMessage_MESSAGE_EDIT: + _cm := mc.ToMatrix(ctx, portal, client, intent, waMsg.ProtocolMessage.EditedMessage, nil, info, isViewOnce, nil) + part = _cm.Parts[0] case waMsg.EncCommentMessage != nil: part = failedCommentPart default: diff --git a/pkg/msgconv/wa-misc.go b/pkg/msgconv/wa-misc.go index 4cc7254..1711b96 100644 --- a/pkg/msgconv/wa-misc.go +++ b/pkg/msgconv/wa-misc.go @@ -155,7 +155,7 @@ func (mc *MessageConverter) convertEphemeralSettingMessage(ctx context.Context, const eventMessageTemplate = ` {{- if .Name -}} -

{{ .Name }}

+

{{ .Name }} {{- if .IsCanceled -}} (Canceled){{- end -}}

{{- end -}} {{- if .StartTime -}}

@@ -181,6 +181,7 @@ var eventMessageTplParsed = exerrors.Must(template.New("eventmessage").Parse(str type eventMessageParams struct { Name string + IsCanceled bool JoinLink string StartTimeISO string StartTime string @@ -193,6 +194,7 @@ type eventMessageParams struct { func (mc *MessageConverter) convertEventMessage(ctx context.Context, msg *waE2E.EventMessage) (*bridgev2.ConvertedMessagePart, *waE2E.ContextInfo) { params := &eventMessageParams{ Name: msg.GetName(), + IsCanceled: msg.GetIsCanceled(), JoinLink: msg.GetJoinLink(), Location: msg.GetLocation().GetName(), DescriptionHTML: template.HTML(parseWAFormattingToHTML(msg.GetDescription(), false)), From f5971eb08c99ea029ee090347562d2328522a924 Mon Sep 17 00:00:00 2001 From: Ping Chen Date: Tue, 16 Sep 2025 08:49:33 +0900 Subject: [PATCH 002/153] bump whatsmeow --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 8958b2c..7d21999 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.1-0.20250912114103-419604f95907 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250912121005-de128e174913 + go.mau.fi/whatsmeow v0.0.0-20250915160606-03f180026b8f golang.org/x/image v0.30.0 golang.org/x/net v0.43.0 golang.org/x/sync v0.16.0 diff --git a/go.sum b/go.sum index 3c81a2b..2101f05 100644 --- a/go.sum +++ b/go.sum @@ -81,8 +81,8 @@ go.mau.fi/util v0.9.1-0.20250912114103-419604f95907 h1:VBApThdwZPIaNKt2sdfTk2Pal go.mau.fi/util v0.9.1-0.20250912114103-419604f95907/go.mod h1:pdL3lg2aaeeHIreGXNnPwhJPXkXdc3ZxsI6le8hOWEA= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20250912121005-de128e174913 h1:7HoljuytFlZuatxwloGqwMi2o1ovqY+IN67oxlpj4Mo= -go.mau.fi/whatsmeow v0.0.0-20250912121005-de128e174913/go.mod h1:HSrGXWxbQ7nIBbLfl4d1XB/qGtEs7GXrQMjfnGLeymM= +go.mau.fi/whatsmeow v0.0.0-20250915160606-03f180026b8f h1:bXMRrrVw+W5dyOkqxn8Q1tIitH97Oe9S5AoVMlcQavQ= +go.mau.fi/whatsmeow v0.0.0-20250915160606-03f180026b8f/go.mod h1:HSrGXWxbQ7nIBbLfl4d1XB/qGtEs7GXrQMjfnGLeymM= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= From 462bce6f3761d6db2b8edc48965f05ae71782bc6 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 29 Sep 2025 19:23:13 +0300 Subject: [PATCH 003/153] handlewhatsapp: decrypt secret encrypted messages Closes #841 --- pkg/connector/handlewhatsapp.go | 10 ++++++++++ pkg/msgconv/wa-misc.go | 4 +++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index f7c2348..752ba33 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -340,6 +340,16 @@ func (wa *WhatsAppClient) handleWAMessage(ctx context.Context, evt *events.Messa evt.Message = decrypted } } + if encMessage := evt.Message.GetSecretEncryptedMessage(); encMessage != nil { + decrypted, err := wa.Client.DecryptSecretEncryptedMessage(ctx, evt) + if err != nil { + wa.UserLogin.Log.Err(err).Str("message_id", evt.Info.ID).Msg("Failed to decrypt message") + return + } + evt.RawMessage = decrypted + evt.UnwrapRaw() + parsedMessageType = getMessageType(evt.Message) + } res := wa.UserLogin.QueueRemoteEvent(&WAMessageEvent{ MessageInfoWrapper: &MessageInfoWrapper{ Info: evt.Info, diff --git a/pkg/msgconv/wa-misc.go b/pkg/msgconv/wa-misc.go index f610715..e6b100e 100644 --- a/pkg/msgconv/wa-misc.go +++ b/pkg/msgconv/wa-misc.go @@ -158,7 +158,7 @@ func (mc *MessageConverter) convertEphemeralSettingMessage(ctx context.Context, const eventMessageTemplate = ` {{- if .Name -}} -

{{ .Name }}

+

{{ .Name }} {{- if .IsCanceled -}} (Canceled){{- end -}}

{{- end -}} {{- if .StartTime -}}

@@ -184,6 +184,7 @@ var eventMessageTplParsed = exerrors.Must(template.New("eventmessage").Parse(str type eventMessageParams struct { Name string + IsCanceled bool JoinLink string StartTimeISO string StartTime string @@ -196,6 +197,7 @@ type eventMessageParams struct { func (mc *MessageConverter) convertEventMessage(ctx context.Context, msg *waE2E.EventMessage) (*bridgev2.ConvertedMessagePart, *waE2E.ContextInfo) { params := &eventMessageParams{ Name: msg.GetName(), + IsCanceled: msg.GetIsCanceled(), JoinLink: msg.GetJoinLink(), Location: msg.GetLocation().GetName(), DescriptionHTML: template.HTML(parseWAFormattingToHTML(msg.GetDescription(), false)), From ca117161e8de83e67cf13f13773a56f6531cc9ae Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 29 Sep 2025 19:26:47 +0300 Subject: [PATCH 004/153] dependencies: update whatsmeow --- go.mod | 2 +- go.sum | 4 ++-- pkg/msgconv/wa-misc.go | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 624d7ac..f28ee2e 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.1 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250919124702-c8bdfd36d05e + go.mau.fi/whatsmeow v0.0.0-20250929162548-7c04e9b206b1 golang.org/x/image v0.31.0 golang.org/x/net v0.44.0 golang.org/x/sync v0.17.0 diff --git a/go.sum b/go.sum index 06e3688..f026f47 100644 --- a/go.sum +++ b/go.sum @@ -81,8 +81,8 @@ go.mau.fi/util v0.9.1 h1:A+XKHRsjKkFi2qOm4RriR1HqY2hoOXNS3WFHaC89r2Y= go.mau.fi/util v0.9.1/go.mod h1:M0bM9SyaOWJniaHs9hxEzz91r5ql6gYq6o1q5O1SsjQ= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20250919124702-c8bdfd36d05e h1:+o9+K5Qyo/490H4NyPVA0Es+aEzmmjaK37/437hV2LM= -go.mau.fi/whatsmeow v0.0.0-20250919124702-c8bdfd36d05e/go.mod h1:dvltpCF0rOHbbur25DHbQ3Ovi747z2Pm11S2M7p1T74= +go.mau.fi/whatsmeow v0.0.0-20250929162548-7c04e9b206b1 h1:JYsRQj8OiqZT6opjhtwUsndTDsHwDtRKaW3AERkGK7E= +go.mau.fi/whatsmeow v0.0.0-20250929162548-7c04e9b206b1/go.mod h1:dvltpCF0rOHbbur25DHbQ3Ovi747z2Pm11S2M7p1T74= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= diff --git a/pkg/msgconv/wa-misc.go b/pkg/msgconv/wa-misc.go index e6b100e..b790701 100644 --- a/pkg/msgconv/wa-misc.go +++ b/pkg/msgconv/wa-misc.go @@ -27,6 +27,7 @@ import ( "github.com/rs/zerolog" "go.mau.fi/util/exerrors" "go.mau.fi/util/ptr" + "go.mau.fi/whatsmeow/proto/waAICommon" "go.mau.fi/whatsmeow/proto/waE2E" "go.mau.fi/whatsmeow/types" "google.golang.org/protobuf/proto" @@ -264,7 +265,7 @@ func (mc *MessageConverter) convertRichResponseMessage(ctx context.Context, msg var body strings.Builder for i, submsg := range msg.GetSubmessages() { - if submsg.GetMessageType() == waE2E.AIRichResponseMessage_AI_RICH_RESPONSE_TEXT { + if submsg.GetMessageType() == waAICommon.AIRichResponseSubMessageType_AI_RICH_RESPONSE_TEXT { if i > 0 { body.WriteString("\n") } From 3541409ae63c9efab13410ac41ef4d4747bb0031 Mon Sep 17 00:00:00 2001 From: fru-9D Date: Mon, 29 Sep 2025 21:31:51 +0500 Subject: [PATCH 005/153] msgconv/from-matrix: fix filename of documents without caption (#840) --- pkg/msgconv/from-matrix.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pkg/msgconv/from-matrix.go b/pkg/msgconv/from-matrix.go index 7fde6a1..559f3bc 100644 --- a/pkg/msgconv/from-matrix.go +++ b/pkg/msgconv/from-matrix.go @@ -255,9 +255,14 @@ func (mc *MessageConverter) constructMediaMessage( }, } case event.MsgFile: + fileName := content.FileName + if fileName == "" { + fileName = content.Body + } + msg := &waE2E.Message{ DocumentMessage: &waE2E.DocumentMessage{ - FileName: proto.String(content.FileName), + FileName: proto.String(fileName), Caption: proto.String(caption), JPEGThumbnail: thumbnail, From b34c75374d667a27666921677c8c31d0f1e25168 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 1 Oct 2025 00:55:49 +0300 Subject: [PATCH 006/153] dependencies: update whatsmeow --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index f28ee2e..e8d87fc 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.1 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250929162548-7c04e9b206b1 + go.mau.fi/whatsmeow v0.0.0-20250930215512-38f9aaa3ba7c golang.org/x/image v0.31.0 golang.org/x/net v0.44.0 golang.org/x/sync v0.17.0 diff --git a/go.sum b/go.sum index f026f47..c98c5bf 100644 --- a/go.sum +++ b/go.sum @@ -81,8 +81,8 @@ go.mau.fi/util v0.9.1 h1:A+XKHRsjKkFi2qOm4RriR1HqY2hoOXNS3WFHaC89r2Y= go.mau.fi/util v0.9.1/go.mod h1:M0bM9SyaOWJniaHs9hxEzz91r5ql6gYq6o1q5O1SsjQ= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20250929162548-7c04e9b206b1 h1:JYsRQj8OiqZT6opjhtwUsndTDsHwDtRKaW3AERkGK7E= -go.mau.fi/whatsmeow v0.0.0-20250929162548-7c04e9b206b1/go.mod h1:dvltpCF0rOHbbur25DHbQ3Ovi747z2Pm11S2M7p1T74= +go.mau.fi/whatsmeow v0.0.0-20250930215512-38f9aaa3ba7c h1:mlDr3/zLUCf8aylmGwds0mnAb4SNYimpb43ylQQlY3Q= +go.mau.fi/whatsmeow v0.0.0-20250930215512-38f9aaa3ba7c/go.mod h1:dvltpCF0rOHbbur25DHbQ3Ovi747z2Pm11S2M7p1T74= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= From b4a929b7aa11a9bf092491ebacaf2faf12fcb135 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 1 Oct 2025 14:59:46 +0300 Subject: [PATCH 007/153] chatinfo: flag group resyncs as excluded from timeline --- go.mod | 4 ++-- go.sum | 8 ++++---- pkg/connector/chatinfo.go | 3 ++- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index e8d87fc..3803e49 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ toolchain go1.25.1 require ( github.com/lib/pq v1.10.9 github.com/rs/zerolog v1.34.0 - go.mau.fi/util v0.9.1 + go.mau.fi/util v0.9.2-0.20251001114608-d99877b9cc10 go.mau.fi/webp v0.2.0 go.mau.fi/whatsmeow v0.0.0-20250930215512-38f9aaa3ba7c golang.org/x/image v0.31.0 @@ -15,7 +15,7 @@ require ( golang.org/x/sync v0.17.0 google.golang.org/protobuf v1.36.9 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.25.2-0.20250924172949-cf29b07f32ce + maunium.net/go/mautrix v0.25.2-0.20251001115535-dd778ae0cdaf ) require ( diff --git a/go.sum b/go.sum index c98c5bf..1ba53fd 100644 --- a/go.sum +++ b/go.sum @@ -77,8 +77,8 @@ github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA= github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= go.mau.fi/libsignal v0.2.0 h1:oRXj3OHhEJq51BFEM8/50UZblmWiTYH93hsNTPcbk90= go.mau.fi/libsignal v0.2.0/go.mod h1:tvjoDsMejgT38CXTXwqaYu8itBiY8O2Mb6biWvZBb9k= -go.mau.fi/util v0.9.1 h1:A+XKHRsjKkFi2qOm4RriR1HqY2hoOXNS3WFHaC89r2Y= -go.mau.fi/util v0.9.1/go.mod h1:M0bM9SyaOWJniaHs9hxEzz91r5ql6gYq6o1q5O1SsjQ= +go.mau.fi/util v0.9.2-0.20251001114608-d99877b9cc10 h1:EvX/di02gOriKN0xGDJuQ5mgiNdAF4LJc8moffI7Svo= +go.mau.fi/util v0.9.2-0.20251001114608-d99877b9cc10/go.mod h1:M0bM9SyaOWJniaHs9hxEzz91r5ql6gYq6o1q5O1SsjQ= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= go.mau.fi/whatsmeow v0.0.0-20250930215512-38f9aaa3ba7c h1:mlDr3/zLUCf8aylmGwds0mnAb4SNYimpb43ylQQlY3Q= @@ -113,5 +113,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.25.2-0.20250924172949-cf29b07f32ce h1:sRBScG2xa66ERjgrX+7D//HorAhmNHwxB2Kzltg+aUg= -maunium.net/go/mautrix v0.25.2-0.20250924172949-cf29b07f32ce/go.mod h1:iSueLJ/2fBaNrsTObGqi1j0cl/loxrtAjmjay1scYD8= +maunium.net/go/mautrix v0.25.2-0.20251001115535-dd778ae0cdaf h1:prmIYgiziW4A8H2v/TliQ7fis8uTWblabxyPIeLFlNg= +maunium.net/go/mautrix v0.25.2-0.20251001115535-dd778ae0cdaf/go.mod h1:eWXuX2UAGye4AU7i/8Fv2L2Nh7L9kZtuv3R0O0n1KaM= diff --git a/pkg/connector/chatinfo.go b/pkg/connector/chatinfo.go index 5a8bfcf..8d47564 100644 --- a/pkg/connector/chatinfo.go +++ b/pkg/connector/chatinfo.go @@ -275,6 +275,7 @@ func (wa *WhatsAppClient) wrapGroupInfo(ctx context.Context, info *types.GroupIn }, }, }, + ExcludeChangesFromTimeline: true, Disappear: &database.DisappearingSetting{ Type: event.DisappearingTypeAfterSend, Timer: time.Duration(info.DisappearingTimer) * time.Second, @@ -460,7 +461,7 @@ func (wa *WhatsAppClient) makePortalAvatarFetcher(avatarID string, sender types. return false } //lint:ignore SA1019 TODO invent a cleaner way to fetch avatar metadata before updating? - return portal.Internal().UpdateAvatar(ctx, wrappedAvatar, senderIntent, ts) + return portal.Internal().UpdateAvatar(ctx, wrappedAvatar, senderIntent, ts, false) } } From b034552d6f2f828aa03e1599d2998b55d9d50518 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 2 Oct 2025 15:14:19 +0300 Subject: [PATCH 008/153] handlematrix: implement account data interfaces --- go.mod | 2 +- go.sum | 4 +-- pkg/connector/client.go | 2 +- pkg/connector/handlematrix.go | 59 +++++++++++++++++++++++++++++++++++ 4 files changed, 63 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 3803e49..742cc31 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.2-0.20251001114608-d99877b9cc10 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250930215512-38f9aaa3ba7c + go.mau.fi/whatsmeow v0.0.0-20251002120845-b3cd0a5002d4 golang.org/x/image v0.31.0 golang.org/x/net v0.44.0 golang.org/x/sync v0.17.0 diff --git a/go.sum b/go.sum index 1ba53fd..55c5c0b 100644 --- a/go.sum +++ b/go.sum @@ -81,8 +81,8 @@ go.mau.fi/util v0.9.2-0.20251001114608-d99877b9cc10 h1:EvX/di02gOriKN0xGDJuQ5mgi go.mau.fi/util v0.9.2-0.20251001114608-d99877b9cc10/go.mod h1:M0bM9SyaOWJniaHs9hxEzz91r5ql6gYq6o1q5O1SsjQ= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20250930215512-38f9aaa3ba7c h1:mlDr3/zLUCf8aylmGwds0mnAb4SNYimpb43ylQQlY3Q= -go.mau.fi/whatsmeow v0.0.0-20250930215512-38f9aaa3ba7c/go.mod h1:dvltpCF0rOHbbur25DHbQ3Ovi747z2Pm11S2M7p1T74= +go.mau.fi/whatsmeow v0.0.0-20251002120845-b3cd0a5002d4 h1:uysENrRsp3Ko6LjrOIsNnCT2l3lTibrUkp+CCx1IxG4= +go.mau.fi/whatsmeow v0.0.0-20251002120845-b3cd0a5002d4/go.mod h1:dvltpCF0rOHbbur25DHbQ3Ovi747z2Pm11S2M7p1T74= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= diff --git a/pkg/connector/client.go b/pkg/connector/client.go index dc160ba..087741f 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -197,7 +197,7 @@ func (wa *WhatsAppClient) Connect(ctx context.Context) { zerolog.Ctx(ctx).Err(err).Msg("Failed to update proxy") } wa.startLoops() - wa.Client.BackgroundEventCtx = wa.Main.Bridge.BackgroundCtx + wa.Client.BackgroundEventCtx = wa.UserLogin.Log.WithContext(wa.Main.Bridge.BackgroundCtx) if err := wa.Client.Connect(); err != nil { zerolog.Ctx(ctx).Err(err).Msg("Failed to connect to WhatsApp") state := status.BridgeState{ diff --git a/pkg/connector/handlematrix.go b/pkg/connector/handlematrix.go index c12ca98..31720f4 100644 --- a/pkg/connector/handlematrix.go +++ b/pkg/connector/handlematrix.go @@ -15,6 +15,8 @@ import ( "go.mau.fi/util/ptr" "go.mau.fi/util/variationselector" "go.mau.fi/whatsmeow" + "go.mau.fi/whatsmeow/appstate" + "go.mau.fi/whatsmeow/proto/waCommon" "go.mau.fi/whatsmeow/proto/waE2E" "go.mau.fi/whatsmeow/types" "golang.org/x/image/draw" @@ -40,6 +42,9 @@ var ( _ bridgev2.RoomNameHandlingNetworkAPI = (*WhatsAppClient)(nil) _ bridgev2.RoomTopicHandlingNetworkAPI = (*WhatsAppClient)(nil) _ bridgev2.RoomAvatarHandlingNetworkAPI = (*WhatsAppClient)(nil) + _ bridgev2.MuteHandlingNetworkAPI = (*WhatsAppClient)(nil) + _ bridgev2.TagHandlingNetworkAPI = (*WhatsAppClient)(nil) + _ bridgev2.MarkedUnreadHandlingNetworkAPI = (*WhatsAppClient)(nil) ) func (wa *WhatsAppClient) HandleMatrixPollStart(ctx context.Context, msg *bridgev2.MatrixPollStart) (*bridgev2.MatrixMessageResponse, error) { @@ -566,3 +571,57 @@ func convertRoomAvatar(data []byte) ([]byte, error) { } return buf.Bytes(), nil } + +func (wa *WhatsAppClient) HandleMute(ctx context.Context, msg *bridgev2.MatrixMute) error { + chatJID, err := waid.ParsePortalID(msg.Portal.ID) + if err != nil { + return err + } + mutedUntil := msg.Content.GetMutedUntilTime() + muted := mutedUntil.After(time.Now()) + muteTS := ptr.Ptr(mutedUntil.UnixMilli()) + if !muted || mutedUntil == event.MutedForever { + muteTS = nil + } + return wa.Client.SendAppState(ctx, appstate.BuildMuteAbs(chatJID, muted, muteTS)) +} + +func (wa *WhatsAppClient) HandleRoomTag(ctx context.Context, msg *bridgev2.MatrixRoomTag) error { + chatJID, err := waid.ParsePortalID(msg.Portal.ID) + if err != nil { + return err + } + _, isFavorite := msg.Content.Tags[event.RoomTagFavourite] + return wa.Client.SendAppState(ctx, appstate.BuildPin(chatJID, isFavorite)) +} + +func (wa *WhatsAppClient) HandleMarkedUnread(ctx context.Context, msg *bridgev2.MatrixMarkedUnread) error { + chatJID, err := waid.ParsePortalID(msg.Portal.ID) + if err != nil { + return err + } + msgs, err := wa.Main.Bridge.DB.Message.GetLastNInPortal(ctx, msg.Portal.PortalKey, 1) + if err != nil { + return fmt.Errorf("failed to get last message in portal: %w", err) + } + var lastTS time.Time + var lastKey *waCommon.MessageKey + if len(msgs) == 1 { + lastTS = msgs[0].Timestamp + parsed, _ := waid.ParseMessageID(msgs[0].ID) + if parsed != nil { + fromMe := parsed.Sender.ToNonAD() == wa.JID.ToNonAD() || parsed.Sender.ToNonAD() == wa.GetStore().GetLID().ToNonAD() + var participant *string + if chatJID.Server == types.GroupServer { + participant = ptr.Ptr(parsed.Sender.String()) + } + lastKey = &waCommon.MessageKey{ + RemoteJID: ptr.Ptr(chatJID.String()), + FromMe: &fromMe, + ID: &parsed.ID, + Participant: participant, + } + } + } + return wa.Client.SendAppState(ctx, appstate.BuildMarkChatAsRead(chatJID, msg.Content.Unread, lastTS, lastKey)) +} From aab115344974457119b361284b4852bc2165c974 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 3 Oct 2025 15:05:02 +0300 Subject: [PATCH 009/153] dependencies: update whatsmeow and set PostgresArrayWrapper properly --- go.mod | 2 +- go.sum | 4 ++-- pkg/connector/connector.go | 5 +++++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 742cc31..2119bc6 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.2-0.20251001114608-d99877b9cc10 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20251002120845-b3cd0a5002d4 + go.mau.fi/whatsmeow v0.0.0-20251003120353-0091f66a98cc golang.org/x/image v0.31.0 golang.org/x/net v0.44.0 golang.org/x/sync v0.17.0 diff --git a/go.sum b/go.sum index 55c5c0b..2edcff5 100644 --- a/go.sum +++ b/go.sum @@ -81,8 +81,8 @@ go.mau.fi/util v0.9.2-0.20251001114608-d99877b9cc10 h1:EvX/di02gOriKN0xGDJuQ5mgi go.mau.fi/util v0.9.2-0.20251001114608-d99877b9cc10/go.mod h1:M0bM9SyaOWJniaHs9hxEzz91r5ql6gYq6o1q5O1SsjQ= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20251002120845-b3cd0a5002d4 h1:uysENrRsp3Ko6LjrOIsNnCT2l3lTibrUkp+CCx1IxG4= -go.mau.fi/whatsmeow v0.0.0-20251002120845-b3cd0a5002d4/go.mod h1:dvltpCF0rOHbbur25DHbQ3Ovi747z2Pm11S2M7p1T74= +go.mau.fi/whatsmeow v0.0.0-20251003120353-0091f66a98cc h1:ebVSx8jdPkDiQM/1V2/RA7z/mrIx2PsTjxiBgE51p4I= +go.mau.fi/whatsmeow v0.0.0-20251003120353-0091f66a98cc/go.mod h1:dvltpCF0rOHbbur25DHbQ3Ovi747z2Pm11S2M7p1T74= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= diff --git a/pkg/connector/connector.go b/pkg/connector/connector.go index 1e41e80..61a961d 100644 --- a/pkg/connector/connector.go +++ b/pkg/connector/connector.go @@ -24,6 +24,7 @@ import ( "sync" "sync/atomic" + "github.com/lib/pq" "go.mau.fi/util/dbutil" "go.mau.fi/util/random" "go.mau.fi/whatsmeow" @@ -58,6 +59,10 @@ type WhatsAppConnector struct { stopMediaEditCacheLoop atomic.Pointer[context.CancelFunc] } +func init() { + sqlstore.PostgresArrayWrapper = pq.Array +} + var ( _ bridgev2.NetworkConnector = (*WhatsAppConnector)(nil) _ bridgev2.MaxFileSizeingNetwork = (*WhatsAppConnector)(nil) From 72dae66a472b1232e2194a244434108c0f415221 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 3 Oct 2025 18:49:58 +0300 Subject: [PATCH 010/153] dependencies: update whatsmeow --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 2119bc6..1155162 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.2-0.20251001114608-d99877b9cc10 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20251003120353-0091f66a98cc + go.mau.fi/whatsmeow v0.0.0-20251003154939-d562355c4d82 golang.org/x/image v0.31.0 golang.org/x/net v0.44.0 golang.org/x/sync v0.17.0 diff --git a/go.sum b/go.sum index 2edcff5..236c07f 100644 --- a/go.sum +++ b/go.sum @@ -81,8 +81,8 @@ go.mau.fi/util v0.9.2-0.20251001114608-d99877b9cc10 h1:EvX/di02gOriKN0xGDJuQ5mgi go.mau.fi/util v0.9.2-0.20251001114608-d99877b9cc10/go.mod h1:M0bM9SyaOWJniaHs9hxEzz91r5ql6gYq6o1q5O1SsjQ= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20251003120353-0091f66a98cc h1:ebVSx8jdPkDiQM/1V2/RA7z/mrIx2PsTjxiBgE51p4I= -go.mau.fi/whatsmeow v0.0.0-20251003120353-0091f66a98cc/go.mod h1:dvltpCF0rOHbbur25DHbQ3Ovi747z2Pm11S2M7p1T74= +go.mau.fi/whatsmeow v0.0.0-20251003154939-d562355c4d82 h1:sLwBH2Q70EoOt8uCZb1gCKGwp2hdZG+ObqpXHWJ4sNk= +go.mau.fi/whatsmeow v0.0.0-20251003154939-d562355c4d82/go.mod h1:dvltpCF0rOHbbur25DHbQ3Ovi747z2Pm11S2M7p1T74= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= From 03f04fe893bf9fbcb0be0ff13efd54928ee84cbc Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sat, 4 Oct 2025 16:00:23 +0300 Subject: [PATCH 011/153] dependencies: update whatsmeow --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 1155162..2c0b0d5 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.2-0.20251001114608-d99877b9cc10 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20251003154939-d562355c4d82 + go.mau.fi/whatsmeow v0.0.0-20251004125807-565fd64f96bd golang.org/x/image v0.31.0 golang.org/x/net v0.44.0 golang.org/x/sync v0.17.0 diff --git a/go.sum b/go.sum index 236c07f..031348b 100644 --- a/go.sum +++ b/go.sum @@ -81,8 +81,8 @@ go.mau.fi/util v0.9.2-0.20251001114608-d99877b9cc10 h1:EvX/di02gOriKN0xGDJuQ5mgi go.mau.fi/util v0.9.2-0.20251001114608-d99877b9cc10/go.mod h1:M0bM9SyaOWJniaHs9hxEzz91r5ql6gYq6o1q5O1SsjQ= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20251003154939-d562355c4d82 h1:sLwBH2Q70EoOt8uCZb1gCKGwp2hdZG+ObqpXHWJ4sNk= -go.mau.fi/whatsmeow v0.0.0-20251003154939-d562355c4d82/go.mod h1:dvltpCF0rOHbbur25DHbQ3Ovi747z2Pm11S2M7p1T74= +go.mau.fi/whatsmeow v0.0.0-20251004125807-565fd64f96bd h1:bIvudPiVur5JhbOunTZ/ozbsiqIZJQNLBYsYY8ek46M= +go.mau.fi/whatsmeow v0.0.0-20251004125807-565fd64f96bd/go.mod h1:dvltpCF0rOHbbur25DHbQ3Ovi747z2Pm11S2M7p1T74= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= From a4fb0844f503f8a3d2fb07bfeb52db1664652288 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sat, 4 Oct 2025 20:12:05 +0300 Subject: [PATCH 012/153] client: enable synchronous acks and decrypted event buffer for everyone --- pkg/connector/client.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/connector/client.go b/pkg/connector/client.go index 087741f..1787a0a 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -73,9 +73,9 @@ func (wa *WhatsAppConnector) LoadUserLogin(ctx context.Context, login *bridgev2. log := w.UserLogin.Log.With().Str("component", "whatsmeow").Logger() w.Client = whatsmeow.NewClient(w.Device, waLog.Zerolog(log)) w.Client.AddEventHandlerWithSuccessStatus(w.handleWAEvent) + w.Client.EnableDecryptedEventBuffer = true + w.Client.SynchronousAck = true if bridgev2.PortalEventBuffer == 0 { - w.Client.SynchronousAck = true - w.Client.EnableDecryptedEventBuffer = true w.Client.ManualHistorySyncDownload = true } w.Client.SendReportingTokens = true From 562e2422dd3044564cdcc86a989e78ad911f793c Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sat, 4 Oct 2025 20:33:19 +0300 Subject: [PATCH 013/153] dependencies: update whatsmeow --- go.mod | 6 +++--- go.sum | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/go.mod b/go.mod index 2c0b0d5..6eb2e5a 100644 --- a/go.mod +++ b/go.mod @@ -9,11 +9,11 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.2-0.20251001114608-d99877b9cc10 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20251004125807-565fd64f96bd + go.mau.fi/whatsmeow v0.0.0-20251004173248-359e39387ad3 golang.org/x/image v0.31.0 golang.org/x/net v0.44.0 golang.org/x/sync v0.17.0 - google.golang.org/protobuf v1.36.9 + google.golang.org/protobuf v1.36.10 gopkg.in/yaml.v3 v3.0.1 maunium.net/go/mautrix v0.25.2-0.20251001115535-dd778ae0cdaf ) @@ -40,7 +40,7 @@ require ( github.com/tidwall/sjson v1.2.5 // indirect github.com/vektah/gqlparser/v2 v2.5.27 // indirect github.com/yuin/goldmark v1.7.13 // indirect - go.mau.fi/libsignal v0.2.0 // indirect + go.mau.fi/libsignal v0.2.1-0.20251004173110-6e0a3f2435ed // indirect go.mau.fi/zeroconfig v0.2.0 // indirect golang.org/x/crypto v0.42.0 // indirect golang.org/x/exp v0.0.0-20250911091902-df9299821621 // indirect diff --git a/go.sum b/go.sum index 031348b..2d1a8b9 100644 --- a/go.sum +++ b/go.sum @@ -18,8 +18,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/elliotchance/orderedmap/v3 v3.1.0 h1:j4DJ5ObEmMBt/lcwIecKcoRxIQUEnw0L804lXYDt/pg= github.com/elliotchance/orderedmap/v3 v3.1.0/go.mod h1:G+Hc2RwaZvJMcS4JpGCOyViCnGeKf0bTYCGTO4uhjSo= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= @@ -75,14 +75,14 @@ github.com/vektah/gqlparser/v2 v2.5.27 h1:RHPD3JOplpk5mP5JGX8RKZkt2/Vwj/PZv0HxTd github.com/vektah/gqlparser/v2 v2.5.27/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo= github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA= github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= -go.mau.fi/libsignal v0.2.0 h1:oRXj3OHhEJq51BFEM8/50UZblmWiTYH93hsNTPcbk90= -go.mau.fi/libsignal v0.2.0/go.mod h1:tvjoDsMejgT38CXTXwqaYu8itBiY8O2Mb6biWvZBb9k= +go.mau.fi/libsignal v0.2.1-0.20251004173110-6e0a3f2435ed h1:f44xyYgZBCEic3OiKY4eYfDA2b3kBB/LlgVha4NpjRs= +go.mau.fi/libsignal v0.2.1-0.20251004173110-6e0a3f2435ed/go.mod h1:iVvjrHyfQqWajOUaMEsIfo3IqgVMrhWcPiiEzk7NgoU= go.mau.fi/util v0.9.2-0.20251001114608-d99877b9cc10 h1:EvX/di02gOriKN0xGDJuQ5mgiNdAF4LJc8moffI7Svo= go.mau.fi/util v0.9.2-0.20251001114608-d99877b9cc10/go.mod h1:M0bM9SyaOWJniaHs9hxEzz91r5ql6gYq6o1q5O1SsjQ= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20251004125807-565fd64f96bd h1:bIvudPiVur5JhbOunTZ/ozbsiqIZJQNLBYsYY8ek46M= -go.mau.fi/whatsmeow v0.0.0-20251004125807-565fd64f96bd/go.mod h1:dvltpCF0rOHbbur25DHbQ3Ovi747z2Pm11S2M7p1T74= +go.mau.fi/whatsmeow v0.0.0-20251004173248-359e39387ad3 h1:dgZ3Pz/AIsx3Yc70wnD9WsgtkD/FrokTY7ACCI7JhO0= +go.mau.fi/whatsmeow v0.0.0-20251004173248-359e39387ad3/go.mod h1:Erq9GGhe5DuIGL9i2gp2LH4nEYkBB+lsgdu3i4KcvPU= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= @@ -102,8 +102,8 @@ golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= -google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= -google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= From 425556d0fa511bd2c898469f55de10c98cd912f5 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sat, 4 Oct 2025 20:45:38 +0300 Subject: [PATCH 014/153] client: disable decrypted event buffer --- pkg/connector/client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/connector/client.go b/pkg/connector/client.go index 1787a0a..6196e16 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -73,9 +73,9 @@ func (wa *WhatsAppConnector) LoadUserLogin(ctx context.Context, login *bridgev2. log := w.UserLogin.Log.With().Str("component", "whatsmeow").Logger() w.Client = whatsmeow.NewClient(w.Device, waLog.Zerolog(log)) w.Client.AddEventHandlerWithSuccessStatus(w.handleWAEvent) - w.Client.EnableDecryptedEventBuffer = true w.Client.SynchronousAck = true if bridgev2.PortalEventBuffer == 0 { + w.Client.EnableDecryptedEventBuffer = true w.Client.ManualHistorySyncDownload = true } w.Client.SendReportingTokens = true From 6fbd3455247a37fb586fa8d14839c737955d1848 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 5 Oct 2025 11:32:04 +0300 Subject: [PATCH 015/153] dependencies: update whatsmeow --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 6eb2e5a..8e7cd87 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.2-0.20251001114608-d99877b9cc10 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20251004173248-359e39387ad3 + go.mau.fi/whatsmeow v0.0.0-20251005083110-4fe97da162dc golang.org/x/image v0.31.0 golang.org/x/net v0.44.0 golang.org/x/sync v0.17.0 diff --git a/go.sum b/go.sum index 2d1a8b9..0ae65fc 100644 --- a/go.sum +++ b/go.sum @@ -81,8 +81,8 @@ go.mau.fi/util v0.9.2-0.20251001114608-d99877b9cc10 h1:EvX/di02gOriKN0xGDJuQ5mgi go.mau.fi/util v0.9.2-0.20251001114608-d99877b9cc10/go.mod h1:M0bM9SyaOWJniaHs9hxEzz91r5ql6gYq6o1q5O1SsjQ= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20251004173248-359e39387ad3 h1:dgZ3Pz/AIsx3Yc70wnD9WsgtkD/FrokTY7ACCI7JhO0= -go.mau.fi/whatsmeow v0.0.0-20251004173248-359e39387ad3/go.mod h1:Erq9GGhe5DuIGL9i2gp2LH4nEYkBB+lsgdu3i4KcvPU= +go.mau.fi/whatsmeow v0.0.0-20251005083110-4fe97da162dc h1:O19PRYoEZJyC7BgUM9PSillFLAkABiBK7AMORNwwK3Q= +go.mau.fi/whatsmeow v0.0.0-20251005083110-4fe97da162dc/go.mod h1:Erq9GGhe5DuIGL9i2gp2LH4nEYkBB+lsgdu3i4KcvPU= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= From f7fa9fe142fdacb598e941940bccabcfb62ef661 Mon Sep 17 00:00:00 2001 From: Kishan Bagaria <1093313+KishanBagaria@users.noreply.github.com> Date: Sun, 5 Oct 2025 20:58:58 +0530 Subject: [PATCH 016/153] msgconv: fix view once placeholder copy when you send instead of receive (#848) Co-authored-by: Rajeh Taher --- pkg/connector/events.go | 6 +++++- pkg/msgconv/wa-media.go | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/pkg/connector/events.go b/pkg/connector/events.go index 46545fd..f371cc0 100644 --- a/pkg/connector/events.go +++ b/pkg/connector/events.go @@ -340,9 +340,13 @@ func (evt *WAUndecryptableMessage) ConvertMessage(ctx context.Context, portal *b } content := &undecryptableMessageContent if evt.Type == events.UnavailableTypeViewOnce { + body := "You received a view once message. For added privacy, you can only open it on the WhatsApp app." + if evt.Info.IsFromMe { + body = "You sent a view once message from another device." + } content = &event.MessageEventContent{ MsgType: event.MsgNotice, - Body: "You received a view once message. For added privacy, you can only open it on the WhatsApp app.", + Body: body, } } // TODO thread root for comments diff --git a/pkg/msgconv/wa-media.go b/pkg/msgconv/wa-media.go index d95d8f9..647e98f 100644 --- a/pkg/msgconv/wa-media.go +++ b/pkg/msgconv/wa-media.go @@ -54,11 +54,15 @@ func (mc *MessageConverter) convertMediaMessage( cachedPart *bridgev2.ConvertedMessagePart, ) (part *bridgev2.ConvertedMessagePart, contextInfo *waE2E.ContextInfo) { if mc.DisableViewOnce && isViewOnce { + body := "You received a view once message. For added privacy, you can only open it on the WhatsApp app." + if messageInfo.IsFromMe { + body = "You sent a view once message from another device." + } return &bridgev2.ConvertedMessagePart{ Type: event.EventMessage, Content: &event.MessageEventContent{ MsgType: event.MsgNotice, - Body: fmt.Sprintf("You received a view once %s. For added privacy, you can only open it on the WhatsApp app.", typeName), + Body: body, }, }, nil } From 2c38e786a27ae55583b28e2cf7fb48b1baf1a264 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 6 Oct 2025 16:52:00 +0300 Subject: [PATCH 017/153] changelog: update --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d32234d..9dc6c9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +# v25.10 (unreleased) + +* Switched to calendar versioning. +* Added support for bridging event edits. +* Fixed backfill creating incorrect disappearing timer change notices. +* Fixed previous messages not being marked as read when sending a new message. +* Fixed incoming call notices with LID addressing going into different DM room. + # v0.12.5 (2025-09-16) * Removed legacy provisioning API and database legacy migration. From 1385a4f93f9e9e4540adabd0246c497d41857428 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 10 Oct 2025 01:38:58 +0300 Subject: [PATCH 018/153] connector: fix hardcoded_wa_version in log --- pkg/connector/connector.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/connector/connector.go b/pkg/connector/connector.go index 61a961d..4e50ca5 100644 --- a/pkg/connector/connector.go +++ b/pkg/connector/connector.go @@ -159,6 +159,8 @@ func (wa *WhatsAppConnector) Stop() { const kvWAVersion = "whatsapp_web_version" +var hardcodedWAVersion = store.GetWAVersion() + func (wa *WhatsAppConnector) onFirstBackgroundConnect() { verStr := wa.Bridge.DB.KV.Get(wa.Bridge.BackgroundCtx, kvWAVersion) if verStr == "" { @@ -171,7 +173,7 @@ func (wa *WhatsAppConnector) onFirstBackgroundConnect() { return } wa.Bridge.Log.Debug(). - Stringer("hardcoded_version", store.GetWAVersion()). + Stringer("hardcoded_version", hardcodedWAVersion). Stringer("cached_version", ver). Msg("Using cached WhatsApp web version number") store.SetWAVersion(ver) @@ -184,7 +186,7 @@ func (wa *WhatsAppConnector) onFirstClientConnect() { wa.Bridge.Log.Err(err).Msg("Failed to get latest WhatsApp web version number") } else { wa.Bridge.Log.Debug(). - Stringer("hardcoded_version", store.GetWAVersion()). + Stringer("hardcoded_version", hardcodedWAVersion). Stringer("latest_version", *ver). Msg("Got latest WhatsApp web version number") store.SetWAVersion(*ver) From dba863c38a1b0d415084c003c546c4774b27612c Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 10 Oct 2025 14:40:08 +0300 Subject: [PATCH 019/153] dependencies: update whatsmeow --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 8e7cd87..2169c87 100644 --- a/go.mod +++ b/go.mod @@ -7,9 +7,9 @@ toolchain go1.25.1 require ( github.com/lib/pq v1.10.9 github.com/rs/zerolog v1.34.0 - go.mau.fi/util v0.9.2-0.20251001114608-d99877b9cc10 + go.mau.fi/util v0.9.2-0.20251005111801-c13b66219cee go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20251005083110-4fe97da162dc + go.mau.fi/whatsmeow v0.0.0-20251010113933-5806d60827b0 golang.org/x/image v0.31.0 golang.org/x/net v0.44.0 golang.org/x/sync v0.17.0 diff --git a/go.sum b/go.sum index 0ae65fc..1794a46 100644 --- a/go.sum +++ b/go.sum @@ -77,12 +77,12 @@ github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA= github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= go.mau.fi/libsignal v0.2.1-0.20251004173110-6e0a3f2435ed h1:f44xyYgZBCEic3OiKY4eYfDA2b3kBB/LlgVha4NpjRs= go.mau.fi/libsignal v0.2.1-0.20251004173110-6e0a3f2435ed/go.mod h1:iVvjrHyfQqWajOUaMEsIfo3IqgVMrhWcPiiEzk7NgoU= -go.mau.fi/util v0.9.2-0.20251001114608-d99877b9cc10 h1:EvX/di02gOriKN0xGDJuQ5mgiNdAF4LJc8moffI7Svo= -go.mau.fi/util v0.9.2-0.20251001114608-d99877b9cc10/go.mod h1:M0bM9SyaOWJniaHs9hxEzz91r5ql6gYq6o1q5O1SsjQ= +go.mau.fi/util v0.9.2-0.20251005111801-c13b66219cee h1:wk1XGP/E1UH3YBqXbb9dajWdO7e9cizgx5+NYkEXT2g= +go.mau.fi/util v0.9.2-0.20251005111801-c13b66219cee/go.mod h1:M0bM9SyaOWJniaHs9hxEzz91r5ql6gYq6o1q5O1SsjQ= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20251005083110-4fe97da162dc h1:O19PRYoEZJyC7BgUM9PSillFLAkABiBK7AMORNwwK3Q= -go.mau.fi/whatsmeow v0.0.0-20251005083110-4fe97da162dc/go.mod h1:Erq9GGhe5DuIGL9i2gp2LH4nEYkBB+lsgdu3i4KcvPU= +go.mau.fi/whatsmeow v0.0.0-20251010113933-5806d60827b0 h1:Q4gIHMOPglOvX8JQ7ewWhoHMhveew2pwnBzmepgJi1w= +go.mau.fi/whatsmeow v0.0.0-20251010113933-5806d60827b0/go.mod h1:CSdGU471Ss7bWunGomSe9RObY0MRxQDvxFH8i5Ndfk4= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= From 9802479e6b67c71d04113cb1721848f0d37c236e Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 16 Oct 2025 12:05:32 +0200 Subject: [PATCH 020/153] Bump version to v25.10 --- CHANGELOG.md | 2 +- cmd/mautrix-whatsapp/main.go | 3 ++- go.mod | 22 ++++++++++---------- go.sum | 40 ++++++++++++++++++------------------ 4 files changed, 34 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9dc6c9e..7e65fef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# v25.10 (unreleased) +# v25.10 * Switched to calendar versioning. * Added support for bridging event edits. diff --git a/cmd/mautrix-whatsapp/main.go b/cmd/mautrix-whatsapp/main.go index 068e67d..a3e0665 100644 --- a/cmd/mautrix-whatsapp/main.go +++ b/cmd/mautrix-whatsapp/main.go @@ -18,7 +18,8 @@ var m = mxmain.BridgeMain{ Name: "mautrix-whatsapp", URL: "https://github.com/mautrix/whatsapp", Description: "A Matrix-WhatsApp puppeting bridge.", - Version: "0.12.5", + Version: "25.10", + SemCalVer: true, Connector: &connector.WhatsAppConnector{}, } diff --git a/go.mod b/go.mod index 2169c87..20a595e 100644 --- a/go.mod +++ b/go.mod @@ -2,20 +2,20 @@ module go.mau.fi/mautrix-whatsapp go 1.24.0 -toolchain go1.25.1 +toolchain go1.25.3 require ( github.com/lib/pq v1.10.9 github.com/rs/zerolog v1.34.0 - go.mau.fi/util v0.9.2-0.20251005111801-c13b66219cee + go.mau.fi/util v0.9.2 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20251010113933-5806d60827b0 - golang.org/x/image v0.31.0 - golang.org/x/net v0.44.0 + go.mau.fi/whatsmeow v0.0.0-20251016095441-02c50743e601 + golang.org/x/image v0.32.0 + golang.org/x/net v0.46.0 golang.org/x/sync v0.17.0 google.golang.org/protobuf v1.36.10 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.25.2-0.20251001115535-dd778ae0cdaf + maunium.net/go/mautrix v0.25.2 ) require ( @@ -40,12 +40,12 @@ require ( github.com/tidwall/sjson v1.2.5 // indirect github.com/vektah/gqlparser/v2 v2.5.27 // indirect github.com/yuin/goldmark v1.7.13 // indirect - go.mau.fi/libsignal v0.2.1-0.20251004173110-6e0a3f2435ed // indirect + go.mau.fi/libsignal v0.2.1 // indirect go.mau.fi/zeroconfig v0.2.0 // indirect - golang.org/x/crypto v0.42.0 // indirect - golang.org/x/exp v0.0.0-20250911091902-df9299821621 // indirect - golang.org/x/sys v0.36.0 // indirect - golang.org/x/text v0.29.0 // indirect + golang.org/x/crypto v0.43.0 // indirect + golang.org/x/exp v0.0.0-20251009144603-d2f985daa21b // indirect + golang.org/x/sys v0.37.0 // indirect + golang.org/x/text v0.30.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect maunium.net/go/mauflag v1.0.0 // indirect diff --git a/go.sum b/go.sum index 1794a46..6d02a06 100644 --- a/go.sum +++ b/go.sum @@ -75,33 +75,33 @@ github.com/vektah/gqlparser/v2 v2.5.27 h1:RHPD3JOplpk5mP5JGX8RKZkt2/Vwj/PZv0HxTd github.com/vektah/gqlparser/v2 v2.5.27/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo= github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA= github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= -go.mau.fi/libsignal v0.2.1-0.20251004173110-6e0a3f2435ed h1:f44xyYgZBCEic3OiKY4eYfDA2b3kBB/LlgVha4NpjRs= -go.mau.fi/libsignal v0.2.1-0.20251004173110-6e0a3f2435ed/go.mod h1:iVvjrHyfQqWajOUaMEsIfo3IqgVMrhWcPiiEzk7NgoU= -go.mau.fi/util v0.9.2-0.20251005111801-c13b66219cee h1:wk1XGP/E1UH3YBqXbb9dajWdO7e9cizgx5+NYkEXT2g= -go.mau.fi/util v0.9.2-0.20251005111801-c13b66219cee/go.mod h1:M0bM9SyaOWJniaHs9hxEzz91r5ql6gYq6o1q5O1SsjQ= +go.mau.fi/libsignal v0.2.1 h1:vRZG4EzTn70XY6Oh/pVKrQGuMHBkAWlGRC22/85m9L0= +go.mau.fi/libsignal v0.2.1/go.mod h1:iVvjrHyfQqWajOUaMEsIfo3IqgVMrhWcPiiEzk7NgoU= +go.mau.fi/util v0.9.2 h1:+S4Z03iCsGqU2WY8X2gySFsFjaLlUHFRDVCYvVwynKM= +go.mau.fi/util v0.9.2/go.mod h1:055elBBCJSdhRsmub7ci9hXZPgGr1U6dYg44cSgRgoU= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20251010113933-5806d60827b0 h1:Q4gIHMOPglOvX8JQ7ewWhoHMhveew2pwnBzmepgJi1w= -go.mau.fi/whatsmeow v0.0.0-20251010113933-5806d60827b0/go.mod h1:CSdGU471Ss7bWunGomSe9RObY0MRxQDvxFH8i5Ndfk4= +go.mau.fi/whatsmeow v0.0.0-20251016095441-02c50743e601 h1:6tytDOaqizeBVG3OdEojIiJtZXY3DWMvFKNkjbxq4tI= +go.mau.fi/whatsmeow v0.0.0-20251016095441-02c50743e601/go.mod h1:VJq+D05Fe5EroZxs2StEYD/AsWJO2aQ7Niucz7lCvao= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= -golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= -golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= -golang.org/x/exp v0.0.0-20250911091902-df9299821621 h1:2id6c1/gto0kaHYyrixvknJ8tUK/Qs5IsmBtrc+FtgU= -golang.org/x/exp v0.0.0-20250911091902-df9299821621/go.mod h1:TwQYMMnGpvZyc+JpB/UAuTNIsVJifOlSkrZkhcvpVUk= -golang.org/x/image v0.31.0 h1:mLChjE2MV6g1S7oqbXC0/UcKijjm5fnJLUYKIYrLESA= -golang.org/x/image v0.31.0/go.mod h1:R9ec5Lcp96v9FTF+ajwaH3uGxPH4fKfHHAVbUILxghA= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= +golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= +golang.org/x/exp v0.0.0-20251009144603-d2f985daa21b h1:18qgiDvlvH7kk8Ioa8Ov+K6xCi0GMvmGfGW0sgd/SYA= +golang.org/x/exp v0.0.0-20251009144603-d2f985daa21b/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70= +golang.org/x/image v0.32.0 h1:6lZQWq75h7L5IWNk0r+SCpUJ6tUVd3v4ZHnbRKLkUDQ= +golang.org/x/image v0.32.0/go.mod h1:/R37rrQmKXtO6tYXAjtDLwQgFLHmhW+V6ayXlxzP2Pc= +golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= +golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= -golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= -golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= +golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= +golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -113,5 +113,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.25.2-0.20251001115535-dd778ae0cdaf h1:prmIYgiziW4A8H2v/TliQ7fis8uTWblabxyPIeLFlNg= -maunium.net/go/mautrix v0.25.2-0.20251001115535-dd778ae0cdaf/go.mod h1:eWXuX2UAGye4AU7i/8Fv2L2Nh7L9kZtuv3R0O0n1KaM= +maunium.net/go/mautrix v0.25.2 h1:CUG23zp754yGOTMh9Q4mVSENS9FyweE/G+6ZsPDMCUU= +maunium.net/go/mautrix v0.25.2/go.mod h1:EWgYyp2iFZP7pnSm+rufHlO8YVnA2KnoNBDpwekiAwI= From 91b2a32b3c38489ac16401f12d37c6e3ec1cf18c Mon Sep 17 00:00:00 2001 From: Conan Date: Fri, 17 Oct 2025 07:27:05 +0800 Subject: [PATCH 021/153] handlematrix: implement DeleteChatHandlingNetworkAPI (#851) --- pkg/connector/capabilities.go | 5 +++-- pkg/connector/handlematrix.go | 38 ++++++++++++++++++++++++++++------- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/pkg/connector/capabilities.go b/pkg/connector/capabilities.go index 095459f..09307e2 100644 --- a/pkg/connector/capabilities.go +++ b/pkg/connector/capabilities.go @@ -50,7 +50,7 @@ func (wa *WhatsAppConnector) GetCapabilities() *bridgev2.NetworkGeneralCapabilit } func (wa *WhatsAppConnector) GetBridgeInfoVersion() (info, caps int) { - return 1, 4 + return 1, 5 } const WAMaxFileSize = 2000 * 1024 * 1024 @@ -65,7 +65,7 @@ func supportedIfFFmpeg() event.CapabilitySupportLevel { } func capID() string { - base := "fi.mau.whatsapp.capabilities.2025_08_25+1" + base := "fi.mau.whatsapp.capabilities.2025_10_07" if ffmpeg.Supported() { return base + "+ffmpeg" } @@ -176,6 +176,7 @@ var whatsappCaps = &event.RoomFeatures{ ReadReceipts: true, TypingNotifications: true, DisappearingTimer: waDisappearingCap, + DeleteChat: true, } var whatsappCAGCaps *event.RoomFeatures diff --git a/pkg/connector/handlematrix.go b/pkg/connector/handlematrix.go index 31720f4..fe77c37 100644 --- a/pkg/connector/handlematrix.go +++ b/pkg/connector/handlematrix.go @@ -45,6 +45,7 @@ var ( _ bridgev2.MuteHandlingNetworkAPI = (*WhatsAppClient)(nil) _ bridgev2.TagHandlingNetworkAPI = (*WhatsAppClient)(nil) _ bridgev2.MarkedUnreadHandlingNetworkAPI = (*WhatsAppClient)(nil) + _ bridgev2.DeleteChatHandlingNetworkAPI = (*WhatsAppClient)(nil) ) func (wa *WhatsAppClient) HandleMatrixPollStart(ctx context.Context, msg *bridgev2.MatrixPollStart) (*bridgev2.MatrixMessageResponse, error) { @@ -595,14 +596,10 @@ func (wa *WhatsAppClient) HandleRoomTag(ctx context.Context, msg *bridgev2.Matri return wa.Client.SendAppState(ctx, appstate.BuildPin(chatJID, isFavorite)) } -func (wa *WhatsAppClient) HandleMarkedUnread(ctx context.Context, msg *bridgev2.MatrixMarkedUnread) error { - chatJID, err := waid.ParsePortalID(msg.Portal.ID) +func (wa *WhatsAppClient) getLastMessageInfo(ctx context.Context, chatJID types.JID, portalKey networkid.PortalKey) (time.Time, *waCommon.MessageKey, error) { + msgs, err := wa.Main.Bridge.DB.Message.GetLastNInPortal(ctx, portalKey, 1) if err != nil { - return err - } - msgs, err := wa.Main.Bridge.DB.Message.GetLastNInPortal(ctx, msg.Portal.PortalKey, 1) - if err != nil { - return fmt.Errorf("failed to get last message in portal: %w", err) + return time.Time{}, nil, fmt.Errorf("failed to get last message in portal: %w", err) } var lastTS time.Time var lastKey *waCommon.MessageKey @@ -623,5 +620,32 @@ func (wa *WhatsAppClient) HandleMarkedUnread(ctx context.Context, msg *bridgev2. } } } + return lastTS, lastKey, nil +} + +func (wa *WhatsAppClient) HandleMarkedUnread(ctx context.Context, msg *bridgev2.MatrixMarkedUnread) error { + chatJID, err := waid.ParsePortalID(msg.Portal.ID) + if err != nil { + return err + } + lastTS, lastKey, err := wa.getLastMessageInfo(ctx, chatJID, msg.Portal.PortalKey) + if err != nil { + return err + } return wa.Client.SendAppState(ctx, appstate.BuildMarkChatAsRead(chatJID, msg.Content.Unread, lastTS, lastKey)) } + +func (wa *WhatsAppClient) HandleMatrixDeleteChat(ctx context.Context, msg *bridgev2.MatrixDeleteChat) error { + chatJID, err := waid.ParsePortalID(msg.Portal.ID) + if err != nil { + return err + } + lastTS, lastKey, err := wa.getLastMessageInfo(ctx, chatJID, msg.Portal.PortalKey) + if err != nil { + return err + } + if lastKey == nil { + return fmt.Errorf("failed to delete chat: no messages found") + } + return wa.Client.SendAppState(ctx, appstate.BuildDeleteChat(chatJID, lastTS, lastKey)) +} From d188d14c1e7542278744a54a661ef8958e5ef3a7 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 20 Oct 2025 22:30:04 +0300 Subject: [PATCH 022/153] all: remove unsafe direct store access --- cmd/mautrix-whatsapp/legacyprovision.go | 2 +- pkg/connector/handlewhatsapp.go | 4 ++-- pkg/connector/id.go | 2 +- pkg/connector/startchat.go | 2 +- pkg/connector/userinfo.go | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cmd/mautrix-whatsapp/legacyprovision.go b/cmd/mautrix-whatsapp/legacyprovision.go index 2a8479d..ef4d984 100644 --- a/cmd/mautrix-whatsapp/legacyprovision.go +++ b/cmd/mautrix-whatsapp/legacyprovision.go @@ -40,7 +40,7 @@ func legacyProvContacts(w http.ResponseWriter, r *http.Request) { if userLogin == nil { return } - if contacts, err := userLogin.Client.(*connector.WhatsAppClient).Device.Contacts.GetAllContacts(r.Context()); err != nil { + if contacts, err := userLogin.Client.(*connector.WhatsAppClient).GetStore().Contacts.GetAllContacts(r.Context()); err != nil { hlog.FromRequest(r).Err(err).Msg("Failed to fetch all contacts") exhttp.WriteJSONResponse(w, http.StatusInternalServerError, Error{ Error: "Internal server error while fetching contact list", diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index 752ba33..2eb1284 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -264,7 +264,7 @@ func (wa *WhatsAppClient) rerouteWAMessage(ctx context.Context, info *types.Mess Msg("Forced LID DM sender to phone number in own message sent from another device") info.Chat = info.RecipientAlt.ToNonAD() } else if info.Sender.Server == types.BotServer && info.Chat.Server == types.HiddenUserServer { - chatPN, err := wa.Device.LIDs.GetPNForLID(ctx, info.Chat) + chatPN, err := wa.GetStore().LIDs.GetPNForLID(ctx, info.Chat) if err != nil { wa.UserLogin.Log.Err(err). Str("message_id", info.ID). @@ -423,7 +423,7 @@ func (wa *WhatsAppClient) handleWAReceipt(ctx context.Context, evt *events.Recei if !evt.MessageSender.IsEmpty() { messageSender = evt.MessageSender } else if evt.Chat.Server == types.GroupServer && evt.Sender.Server == types.HiddenUserServer { - lid := wa.Device.GetLID() + lid := wa.GetStore().GetLID() if !lid.IsEmpty() { messageSender = lid } diff --git a/pkg/connector/id.go b/pkg/connector/id.go index e956e59..5028b7b 100644 --- a/pkg/connector/id.go +++ b/pkg/connector/id.go @@ -39,7 +39,7 @@ func (wa *WhatsAppClient) makeEventSender(ctx context.Context, id types.JID) bri } else if id.Server == types.DefaultUserServer { senderLoginJID = id } else if id.Server == types.HiddenUserServer { - pn, err := wa.Device.LIDs.GetPNForLID(ctx, id) + pn, err := wa.GetStore().LIDs.GetPNForLID(ctx, id) if err != nil { zerolog.Ctx(ctx).Err(err). Stringer("lid", id). diff --git a/pkg/connector/startchat.go b/pkg/connector/startchat.go index 7e03fc0..a4db5de 100644 --- a/pkg/connector/startchat.go +++ b/pkg/connector/startchat.go @@ -109,7 +109,7 @@ func (wa *WhatsAppConnector) ValidateUserID(id networkid.UserID) bool { func (wa *WhatsAppClient) startChatLIDToPN(ctx context.Context, jid types.JID) (types.JID, error) { if jid.Server == types.HiddenUserServer { - pn, err := wa.Device.LIDs.GetPNForLID(ctx, jid) + pn, err := wa.GetStore().LIDs.GetPNForLID(ctx, jid) if err != nil { return jid, fmt.Errorf("failed to get phone number for lid: %w", err) } else if pn.IsEmpty() { diff --git a/pkg/connector/userinfo.go b/pkg/connector/userinfo.go index c9e2e9f..150a97d 100644 --- a/pkg/connector/userinfo.go +++ b/pkg/connector/userinfo.go @@ -378,9 +378,9 @@ func (wa *WhatsAppClient) syncAltGhostWithInfo(ctx context.Context, jid types.JI var altJID types.JID var err error if jid.Server == types.HiddenUserServer { - altJID, err = wa.Device.LIDs.GetPNForLID(ctx, jid) + altJID, err = wa.GetStore().LIDs.GetPNForLID(ctx, jid) } else if jid.Server == types.DefaultUserServer { - altJID, err = wa.Device.LIDs.GetLIDForPN(ctx, jid) + altJID, err = wa.GetStore().LIDs.GetLIDForPN(ctx, jid) } if err != nil { log.Warn().Err(err). From 4b388237aa8af2f2b0ebe2e8b0119153b9bbe5f7 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 21 Oct 2025 14:18:41 +0300 Subject: [PATCH 023/153] docker: use gitlab dependency proxy for CI builds --- Dockerfile.ci | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Dockerfile.ci b/Dockerfile.ci index 9b7189d..16ac07d 100644 --- a/Dockerfile.ci +++ b/Dockerfile.ci @@ -1,4 +1,6 @@ -FROM alpine:3.22 +ARG DOCKER_HUB="docker.io" + +FROM ${DOCKER_HUB}/alpine:3.22 ENV UID=1337 \ GID=1337 From 4171a95b014b2c2f512ddfb37f4aa43398ef70bd Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 21 Oct 2025 17:16:54 +0300 Subject: [PATCH 024/153] startchat: exclude non-contacts from GetContactList response --- pkg/connector/startchat.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pkg/connector/startchat.go b/pkg/connector/startchat.go index a4db5de..d7c61fc 100644 --- a/pkg/connector/startchat.go +++ b/pkg/connector/startchat.go @@ -164,11 +164,11 @@ func (wa *WhatsAppClient) ResolveIdentifier(ctx context.Context, identifier stri } func (wa *WhatsAppClient) GetContactList(ctx context.Context) ([]*bridgev2.ResolveIdentifierResponse, error) { - return wa.getContactList(ctx, "") + return wa.getContactList(ctx, "", true) } func (wa *WhatsAppClient) SearchUsers(ctx context.Context, query string) ([]*bridgev2.ResolveIdentifierResponse, error) { - return wa.getContactList(ctx, strings.ToLower(query)) + return wa.getContactList(ctx, strings.ToLower(query), false) } func matchesQuery(str string, query string) bool { @@ -178,7 +178,7 @@ func matchesQuery(str string, query string) bool { return strings.Contains(strings.ToLower(str), query) } -func (wa *WhatsAppClient) getContactList(ctx context.Context, filter string) ([]*bridgev2.ResolveIdentifierResponse, error) { +func (wa *WhatsAppClient) getContactList(ctx context.Context, filter string, onlyContacts bool) ([]*bridgev2.ResolveIdentifierResponse, error) { if !wa.IsLoggedIn() { return nil, mautrix.MForbidden.WithMessage("You must be logged in to list contacts") } @@ -188,6 +188,9 @@ func (wa *WhatsAppClient) getContactList(ctx context.Context, filter string) ([] } resp := make([]*bridgev2.ResolveIdentifierResponse, 0, len(contacts)) for jid, contactInfo := range contacts { + if onlyContacts && contactInfo.FirstName == "" { + continue + } if !matchesQuery(contactInfo.PushName, filter) && !matchesQuery(contactInfo.FullName, filter) && !matchesQuery(jid.User, filter) { continue } From 98b7e08f4ed1e9b7868242682151811c0dd8efd1 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 21 Oct 2025 17:42:25 +0300 Subject: [PATCH 025/153] startchat,msgconv: return group create errors and allow sending invites in DMs --- go.mod | 2 +- go.sum | 4 +-- pkg/connector/startchat.go | 35 +++++++++++++++++++++++ pkg/msgconv/from-matrix.go | 57 ++++++++++++++++++++++++++++++++++++-- pkg/msgconv/wa-misc.go | 10 ++++--- pkg/waid/dbmeta.go | 3 ++ 6 files changed, 101 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index 20a595e..245626a 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( golang.org/x/sync v0.17.0 google.golang.org/protobuf v1.36.10 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.25.2 + maunium.net/go/mautrix v0.25.3-0.20251021144018-1aacf6e987b1 ) require ( diff --git a/go.sum b/go.sum index 6d02a06..50995ed 100644 --- a/go.sum +++ b/go.sum @@ -113,5 +113,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.25.2 h1:CUG23zp754yGOTMh9Q4mVSENS9FyweE/G+6ZsPDMCUU= -maunium.net/go/mautrix v0.25.2/go.mod h1:EWgYyp2iFZP7pnSm+rufHlO8YVnA2KnoNBDpwekiAwI= +maunium.net/go/mautrix v0.25.3-0.20251021144018-1aacf6e987b1 h1:jE/W7DZnnD3wWVinyiUkuN8lMBM8AcUiJX5V2fClj3Y= +maunium.net/go/mautrix v0.25.3-0.20251021144018-1aacf6e987b1/go.mod h1:EWgYyp2iFZP7pnSm+rufHlO8YVnA2KnoNBDpwekiAwI= diff --git a/pkg/connector/startchat.go b/pkg/connector/startchat.go index d7c61fc..ad0dd05 100644 --- a/pkg/connector/startchat.go +++ b/pkg/connector/startchat.go @@ -35,6 +35,7 @@ import ( "maunium.net/go/mautrix/event" "maunium.net/go/mautrix/id" + "go.mau.fi/mautrix-whatsapp/pkg/msgconv" "go.mau.fi/mautrix-whatsapp/pkg/waid" ) @@ -249,6 +250,38 @@ func (wa *WhatsAppClient) CreateGroup(ctx context.Context, params *bridgev2.Grou if err != nil { return nil, fmt.Errorf("failed to create group: %w", err) } + failedParticipants := make(map[networkid.UserID]bridgev2.CreateChatFailedParticipant) + filteredParticipants := resp.Participants[:0] + for _, pcp := range resp.Participants { + if pcp.Error != 0 { + var inviteContent *event.Content + if pcp.AddRequest != nil { + inviteContent = &event.Content{ + Raw: map[string]any{ + msgconv.GroupInviteMetaField: &waid.GroupInviteMeta{ + JID: resp.JID, + Code: pcp.AddRequest.Code, + Expiration: pcp.AddRequest.Expiration.Unix(), + Inviter: wa.JID.ToNonAD(), + GroupName: resp.Name, + IsParentGroup: resp.IsParent, + }, + }, + Parsed: &event.MessageEventContent{ + Body: "Invitation to join my WhatsApp group", + MsgType: event.MsgText, + }, + } + } + failedParticipants[waid.MakeUserID(pcp.JID)] = bridgev2.CreateChatFailedParticipant{ + Reason: fmt.Sprintf("error %d", pcp.Error), + InviteContent: inviteContent, + } + } else { + filteredParticipants = append(filteredParticipants, pcp) + } + } + resp.Participants = filteredParticipants portal, err := wa.Main.Bridge.GetPortalByKey(ctx, wa.makeWAPortalKey(resp.JID)) if err != nil { return nil, fmt.Errorf("failed to get portal: %w", err) @@ -322,5 +355,7 @@ func (wa *WhatsAppClient) CreateGroup(ctx context.Context, params *bridgev2.Grou PortalKey: wa.makeWAPortalKey(resp.JID), Portal: portal, PortalInfo: groupInfo, + + FailedParticipants: failedParticipants, }, nil } diff --git a/pkg/msgconv/from-matrix.go b/pkg/msgconv/from-matrix.go index 559f3bc..78b8554 100644 --- a/pkg/msgconv/from-matrix.go +++ b/pkg/msgconv/from-matrix.go @@ -19,6 +19,7 @@ package msgconv import ( "bytes" "context" + "encoding/json" "errors" "fmt" "image" @@ -99,7 +100,11 @@ func (mc *MessageConverter) ToWhatsApp( switch content.MsgType { case event.MsgText, event.MsgNotice, event.MsgEmote: - message = mc.constructTextMessage(ctx, content, contextInfo) + var err error + message, err = mc.constructTextMessage(ctx, content, evt.Content.Raw, contextInfo) + if err != nil { + return nil, nil, err + } case event.MessageType(event.EventSticker.Type), event.MsgImage, event.MsgVideo, event.MsgAudio, event.MsgFile: uploaded, thumbnail, mime, err := mc.reuploadFileToWhatsApp(ctx, content) if err != nil { @@ -304,7 +309,16 @@ func (mc *MessageConverter) parseText(ctx context.Context, content *event.Messag return } -func (mc *MessageConverter) constructTextMessage(ctx context.Context, content *event.MessageEventContent, contextInfo *waE2E.ContextInfo) *waE2E.Message { +func (mc *MessageConverter) constructTextMessage( + ctx context.Context, + content *event.MessageEventContent, + raw map[string]any, + contextInfo *waE2E.ContextInfo, +) (*waE2E.Message, error) { + groupInvite, ok := raw[GroupInviteMetaField].(map[string]any) + if ok { + return mc.constructGroupInviteMessage(ctx, content, groupInvite, contextInfo) + } text, mentions := mc.parseText(ctx, content) if len(mentions) > 0 { contextInfo.MentionedJID = mentions @@ -315,7 +329,44 @@ func (mc *MessageConverter) constructTextMessage(ctx context.Context, content *e } mc.convertURLPreviewToWhatsApp(ctx, content, etm) - return &waE2E.Message{ExtendedTextMessage: etm} + return &waE2E.Message{ExtendedTextMessage: etm}, nil +} + +func (mc *MessageConverter) constructGroupInviteMessage( + ctx context.Context, + content *event.MessageEventContent, + inviteMeta map[string]any, + contextInfo *waE2E.ContextInfo, +) (*waE2E.Message, error) { + payload, err := json.Marshal(inviteMeta) + if err != nil { + return nil, fmt.Errorf("failed to marshal invite meta: %w", err) + } + var parsedInviteMeta waid.GroupInviteMeta + err = json.Unmarshal(payload, &parsedInviteMeta) + if err != nil { + return nil, fmt.Errorf("failed to parse invite meta: %w", err) + } + text, mentions := mc.parseText(ctx, content) + if len(mentions) > 0 { + contextInfo.MentionedJID = mentions + } + groupType := waE2E.GroupInviteMessage_DEFAULT + if parsedInviteMeta.IsParentGroup { + groupType = waE2E.GroupInviteMessage_PARENT + } + return &waE2E.Message{ + GroupInviteMessage: &waE2E.GroupInviteMessage{ + GroupJID: proto.String(parsedInviteMeta.JID.String()), + InviteCode: proto.String(parsedInviteMeta.Code), + InviteExpiration: proto.Int64(parsedInviteMeta.Expiration), + GroupName: proto.String(parsedInviteMeta.GroupName), + JPEGThumbnail: nil, + Caption: proto.String(text), + ContextInfo: contextInfo, + GroupType: groupType.Enum(), + }, + }, nil } func (mc *MessageConverter) convertPill(displayname, mxid, eventID string, ctx format.Context) string { diff --git a/pkg/msgconv/wa-misc.go b/pkg/msgconv/wa-misc.go index b790701..8dcdfb9 100644 --- a/pkg/msgconv/wa-misc.go +++ b/pkg/msgconv/wa-misc.go @@ -88,10 +88,12 @@ func (mc *MessageConverter) convertGroupInviteMessage(ctx context.Context, info template = inviteMsgBroken } else { inviteMeta = &waid.GroupInviteMeta{ - JID: groupJID, - Code: msg.GetInviteCode(), - Expiration: msg.GetInviteExpiration(), - Inviter: info.Sender.ToNonAD(), + JID: groupJID, + Code: msg.GetInviteCode(), + Expiration: msg.GetInviteExpiration(), + Inviter: info.Sender.ToNonAD(), + GroupName: msg.GetGroupName(), + IsParentGroup: msg.GetGroupType() == waE2E.GroupInviteMessage_PARENT, } extraAttrs = map[string]any{ GroupInviteMetaField: inviteMeta, diff --git a/pkg/waid/dbmeta.go b/pkg/waid/dbmeta.go index 81fd895..5f47bdd 100644 --- a/pkg/waid/dbmeta.go +++ b/pkg/waid/dbmeta.go @@ -68,6 +68,9 @@ type GroupInviteMeta struct { Code string `json:"code"` Expiration int64 `json:"expiration,string"` Inviter types.JID `json:"inviter"` + + GroupName string `json:"group_name,omitempty"` + IsParentGroup bool `json:"is_parent_group,omitempty"` } type MessageMetadata struct { From 09153f6acfb1cba13d3323b76eccf8651c150daf Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 21 Oct 2025 18:56:23 +0300 Subject: [PATCH 026/153] startchat: update failed participants format --- go.mod | 2 +- go.sum | 4 ++-- pkg/connector/startchat.go | 9 +++++---- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 245626a..9895a3a 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( golang.org/x/sync v0.17.0 google.golang.org/protobuf v1.36.10 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.25.3-0.20251021144018-1aacf6e987b1 + maunium.net/go/mautrix v0.25.3-0.20251021155549-ef31dae082e5 ) require ( diff --git a/go.sum b/go.sum index 50995ed..336e845 100644 --- a/go.sum +++ b/go.sum @@ -113,5 +113,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.25.3-0.20251021144018-1aacf6e987b1 h1:jE/W7DZnnD3wWVinyiUkuN8lMBM8AcUiJX5V2fClj3Y= -maunium.net/go/mautrix v0.25.3-0.20251021144018-1aacf6e987b1/go.mod h1:EWgYyp2iFZP7pnSm+rufHlO8YVnA2KnoNBDpwekiAwI= +maunium.net/go/mautrix v0.25.3-0.20251021155549-ef31dae082e5 h1:DgOFsVa2evs4ZK6VFpwOrU50oh7PwCWBi7vXPEpW4Dk= +maunium.net/go/mautrix v0.25.3-0.20251021155549-ef31dae082e5/go.mod h1:EWgYyp2iFZP7pnSm+rufHlO8YVnA2KnoNBDpwekiAwI= diff --git a/pkg/connector/startchat.go b/pkg/connector/startchat.go index ad0dd05..ee4ed9b 100644 --- a/pkg/connector/startchat.go +++ b/pkg/connector/startchat.go @@ -250,7 +250,7 @@ func (wa *WhatsAppClient) CreateGroup(ctx context.Context, params *bridgev2.Grou if err != nil { return nil, fmt.Errorf("failed to create group: %w", err) } - failedParticipants := make(map[networkid.UserID]bridgev2.CreateChatFailedParticipant) + failedParticipants := make(map[networkid.UserID]*bridgev2.CreateChatFailedParticipant) filteredParticipants := resp.Participants[:0] for _, pcp := range resp.Participants { if pcp.Error != 0 { @@ -273,9 +273,10 @@ func (wa *WhatsAppClient) CreateGroup(ctx context.Context, params *bridgev2.Grou }, } } - failedParticipants[waid.MakeUserID(pcp.JID)] = bridgev2.CreateChatFailedParticipant{ - Reason: fmt.Sprintf("error %d", pcp.Error), - InviteContent: inviteContent, + failedParticipants[waid.MakeUserID(pcp.JID)] = &bridgev2.CreateChatFailedParticipant{ + Reason: fmt.Sprintf("error %d", pcp.Error), + InviteEventType: event.EventMessage.Type, + InviteContent: inviteContent, } } else { filteredParticipants = append(filteredParticipants, pcp) From a138a7157ba37cad93d982bc347abcc981d3a5ea Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 22 Oct 2025 13:04:21 +0300 Subject: [PATCH 027/153] handle*: fix handling mute forever --- go.mod | 4 ++-- go.sum | 8 ++++---- pkg/connector/handlewhatsapp.go | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 9895a3a..7f14c63 100644 --- a/go.mod +++ b/go.mod @@ -9,13 +9,13 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.2 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20251016095441-02c50743e601 + go.mau.fi/whatsmeow v0.0.0-20251022100208-a81333ed9f79 golang.org/x/image v0.32.0 golang.org/x/net v0.46.0 golang.org/x/sync v0.17.0 google.golang.org/protobuf v1.36.10 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.25.3-0.20251021155549-ef31dae082e5 + maunium.net/go/mautrix v0.25.3-0.20251022100332-e805815e4120 ) require ( diff --git a/go.sum b/go.sum index 336e845..c24693a 100644 --- a/go.sum +++ b/go.sum @@ -81,8 +81,8 @@ go.mau.fi/util v0.9.2 h1:+S4Z03iCsGqU2WY8X2gySFsFjaLlUHFRDVCYvVwynKM= go.mau.fi/util v0.9.2/go.mod h1:055elBBCJSdhRsmub7ci9hXZPgGr1U6dYg44cSgRgoU= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20251016095441-02c50743e601 h1:6tytDOaqizeBVG3OdEojIiJtZXY3DWMvFKNkjbxq4tI= -go.mau.fi/whatsmeow v0.0.0-20251016095441-02c50743e601/go.mod h1:VJq+D05Fe5EroZxs2StEYD/AsWJO2aQ7Niucz7lCvao= +go.mau.fi/whatsmeow v0.0.0-20251022100208-a81333ed9f79 h1:ps7TvlR7BlLgCKDWy9L1eFtwpLcezhvAe4sMUo/gwIY= +go.mau.fi/whatsmeow v0.0.0-20251022100208-a81333ed9f79/go.mod h1:VJq+D05Fe5EroZxs2StEYD/AsWJO2aQ7Niucz7lCvao= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= @@ -113,5 +113,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.25.3-0.20251021155549-ef31dae082e5 h1:DgOFsVa2evs4ZK6VFpwOrU50oh7PwCWBi7vXPEpW4Dk= -maunium.net/go/mautrix v0.25.3-0.20251021155549-ef31dae082e5/go.mod h1:EWgYyp2iFZP7pnSm+rufHlO8YVnA2KnoNBDpwekiAwI= +maunium.net/go/mautrix v0.25.3-0.20251022100332-e805815e4120 h1:9tknT3v95cmHYYCssiJHUWEU/N1rn7Ft1rziTn0bIHU= +maunium.net/go/mautrix v0.25.3-0.20251022100332-e805815e4120/go.mod h1:EWgYyp2iFZP7pnSm+rufHlO8YVnA2KnoNBDpwekiAwI= diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index 2eb1284..c7082f7 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -744,7 +744,7 @@ func (wa *WhatsAppClient) handleWAMute(evt *events.Mute) bool { var mutedUntil time.Time if evt.Action.GetMuted() { mutedUntil = event.MutedForever - if evt.Action.GetMuteEndTimestamp() != 0 { + if evt.Action.GetMuteEndTimestamp() > 0 { mutedUntil = time.Unix(evt.Action.GetMuteEndTimestamp(), 0) } } else { From 0f9dc4a395f20604e3ed3b1847b80c13c722a85a Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 22 Oct 2025 18:57:28 +0300 Subject: [PATCH 028/153] handlewhatsapp: fix deleting space when leaving community --- go.mod | 2 +- go.sum | 4 ++-- pkg/connector/handlewhatsapp.go | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 7f14c63..d1ad9dc 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( golang.org/x/sync v0.17.0 google.golang.org/protobuf v1.36.10 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.25.3-0.20251022100332-e805815e4120 + maunium.net/go/mautrix v0.25.3-0.20251022155641-9fd1e0f87cef ) require ( diff --git a/go.sum b/go.sum index c24693a..8d15958 100644 --- a/go.sum +++ b/go.sum @@ -113,5 +113,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.25.3-0.20251022100332-e805815e4120 h1:9tknT3v95cmHYYCssiJHUWEU/N1rn7Ft1rziTn0bIHU= -maunium.net/go/mautrix v0.25.3-0.20251022100332-e805815e4120/go.mod h1:EWgYyp2iFZP7pnSm+rufHlO8YVnA2KnoNBDpwekiAwI= +maunium.net/go/mautrix v0.25.3-0.20251022155641-9fd1e0f87cef h1:h+v9hjL0+fOmyi12jLC56l7332VDGLkm3+b7Cz/BLh4= +maunium.net/go/mautrix v0.25.3-0.20251022155641-9fd1e0f87cef/go.mod h1:EWgYyp2iFZP7pnSm+rufHlO8YVnA2KnoNBDpwekiAwI= diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index c7082f7..4429d05 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -579,6 +579,7 @@ func (wa *WhatsAppClient) handleWADeleteChat(evt *events.DeleteChat) bool { Timestamp: evt.Timestamp, }, OnlyForMe: true, + Children: true, }).Success } From 1d320e9094b4f59de829f7c55bde2d4695269beb Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 24 Oct 2025 12:45:35 +0300 Subject: [PATCH 029/153] dependencies: update --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index d1ad9dc..b60775a 100644 --- a/go.mod +++ b/go.mod @@ -9,13 +9,13 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.2 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20251022100208-a81333ed9f79 + go.mau.fi/whatsmeow v0.0.0-20251023183934-2ced35dd7e8c golang.org/x/image v0.32.0 golang.org/x/net v0.46.0 golang.org/x/sync v0.17.0 google.golang.org/protobuf v1.36.10 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.25.3-0.20251022155641-9fd1e0f87cef + maunium.net/go/mautrix v0.25.3-0.20251024094209-5d87d14b8858 ) require ( diff --git a/go.sum b/go.sum index 8d15958..a67ba28 100644 --- a/go.sum +++ b/go.sum @@ -81,8 +81,8 @@ go.mau.fi/util v0.9.2 h1:+S4Z03iCsGqU2WY8X2gySFsFjaLlUHFRDVCYvVwynKM= go.mau.fi/util v0.9.2/go.mod h1:055elBBCJSdhRsmub7ci9hXZPgGr1U6dYg44cSgRgoU= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20251022100208-a81333ed9f79 h1:ps7TvlR7BlLgCKDWy9L1eFtwpLcezhvAe4sMUo/gwIY= -go.mau.fi/whatsmeow v0.0.0-20251022100208-a81333ed9f79/go.mod h1:VJq+D05Fe5EroZxs2StEYD/AsWJO2aQ7Niucz7lCvao= +go.mau.fi/whatsmeow v0.0.0-20251023183934-2ced35dd7e8c h1:7QLq7oW+q8U5AU7uhGsVOB2qPD3306fr7oVSni77c+A= +go.mau.fi/whatsmeow v0.0.0-20251023183934-2ced35dd7e8c/go.mod h1:VJq+D05Fe5EroZxs2StEYD/AsWJO2aQ7Niucz7lCvao= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= @@ -113,5 +113,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.25.3-0.20251022155641-9fd1e0f87cef h1:h+v9hjL0+fOmyi12jLC56l7332VDGLkm3+b7Cz/BLh4= -maunium.net/go/mautrix v0.25.3-0.20251022155641-9fd1e0f87cef/go.mod h1:EWgYyp2iFZP7pnSm+rufHlO8YVnA2KnoNBDpwekiAwI= +maunium.net/go/mautrix v0.25.3-0.20251024094209-5d87d14b8858 h1:w4rgGYjb+lA/AIbyOaUAMDQ6KhxyrTxNxZMPljaXoYw= +maunium.net/go/mautrix v0.25.3-0.20251024094209-5d87d14b8858/go.mod h1:EWgYyp2iFZP7pnSm+rufHlO8YVnA2KnoNBDpwekiAwI= From 1cd8d7441adc27428103f26a7e005c137f1b5ed6 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 27 Oct 2025 16:20:47 +0200 Subject: [PATCH 030/153] all: update whatsmeow methods to use contexts --- go.mod | 3 +-- go.sum | 6 ++---- pkg/connector/chatinfo.go | 8 ++++---- pkg/connector/client.go | 18 ++++++++++-------- pkg/connector/commands.go | 16 ++++++++-------- pkg/connector/directmedia.go | 4 ++-- pkg/connector/events.go | 2 +- pkg/connector/handlematrix.go | 16 ++++++++-------- pkg/connector/handlewhatsapp.go | 8 ++++---- pkg/connector/login.go | 2 +- pkg/connector/mediarequest.go | 6 +++--- pkg/connector/proxy.go | 5 ++++- pkg/connector/startchat.go | 10 +++++----- pkg/connector/userinfo.go | 4 ++-- 14 files changed, 55 insertions(+), 53 deletions(-) diff --git a/go.mod b/go.mod index b60775a..971dfba 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.2 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20251023183934-2ced35dd7e8c + go.mau.fi/whatsmeow v0.0.0-20251027141726-3d82d3101dd1 golang.org/x/image v0.32.0 golang.org/x/net v0.46.0 golang.org/x/sync v0.17.0 @@ -25,7 +25,6 @@ require ( github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/elliotchance/orderedmap/v3 v3.1.0 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/gorilla/websocket v1.5.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect diff --git a/go.sum b/go.sum index a67ba28..e991e5b 100644 --- a/go.sum +++ b/go.sum @@ -22,8 +22,6 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -81,8 +79,8 @@ go.mau.fi/util v0.9.2 h1:+S4Z03iCsGqU2WY8X2gySFsFjaLlUHFRDVCYvVwynKM= go.mau.fi/util v0.9.2/go.mod h1:055elBBCJSdhRsmub7ci9hXZPgGr1U6dYg44cSgRgoU= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20251023183934-2ced35dd7e8c h1:7QLq7oW+q8U5AU7uhGsVOB2qPD3306fr7oVSni77c+A= -go.mau.fi/whatsmeow v0.0.0-20251023183934-2ced35dd7e8c/go.mod h1:VJq+D05Fe5EroZxs2StEYD/AsWJO2aQ7Niucz7lCvao= +go.mau.fi/whatsmeow v0.0.0-20251027141726-3d82d3101dd1 h1:xjTxq7OfIV+oVJnsWrXX5lrIt8cSMV6yHppdp2+fVQE= +go.mau.fi/whatsmeow v0.0.0-20251027141726-3d82d3101dd1/go.mod h1:RwBrMQAWCHGzMdDZ6EwjcY4Aj3g8Efx8c7GACTdiAME= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= diff --git a/pkg/connector/chatinfo.go b/pkg/connector/chatinfo.go index 8d47564..c3545f5 100644 --- a/pkg/connector/chatinfo.go +++ b/pkg/connector/chatinfo.go @@ -40,14 +40,14 @@ func (wa *WhatsAppClient) getChatInfo(ctx context.Context, portalJID types.JID, return nil, fmt.Errorf("broadcast list bridging is currently not supported") } case types.GroupServer: - info, err := wa.Client.GetGroupInfo(portalJID) + info, err := wa.Client.GetGroupInfo(ctx, portalJID) if err != nil { return nil, err } wrapped = wa.wrapGroupInfo(ctx, info) wrapped.ExtraUpdates = bridgev2.MergeExtraUpdaters(wrapped.ExtraUpdates, updatePortalLastSyncAt) case types.NewsletterServer: - info, err := wa.Client.GetNewsletterInfo(portalJID) + info, err := wa.Client.GetNewsletterInfo(ctx, portalJID) if err != nil { return nil, err } @@ -419,7 +419,7 @@ func (wa *WhatsAppClient) makePortalAvatarFetcher(avatarID string, sender types. existingID = "" } var wrappedAvatar *bridgev2.Avatar - avatar, err := wa.Client.GetProfilePictureInfo(jid, &whatsmeow.GetProfilePictureParams{ + avatar, err := wa.Client.GetProfilePictureInfo(ctx, jid, &whatsmeow.GetProfilePictureParams{ ExistingID: existingID, IsCommunity: portal.RoomType == database.RoomTypeSpace, }) @@ -492,7 +492,7 @@ func (wa *WhatsAppClient) wrapNewsletterInfo(ctx context.Context, info *types.Ne } else if info.ThreadMeta.Preview.ID != "" { avatar.ID = networkid.AvatarID(info.ThreadMeta.Preview.ID) avatar.Get = func(ctx context.Context) ([]byte, error) { - meta, err := wa.Client.GetNewsletterInfo(info.ID) + meta, err := wa.Client.GetNewsletterInfo(ctx, info.ID) if err != nil { return nil, fmt.Errorf("failed to fetch full res avatar info: %w", err) } else if meta.ThreadMeta.Picture == nil { diff --git a/pkg/connector/client.go b/pkg/connector/client.go index 6196e16..7c5c6e9 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -196,9 +196,12 @@ func (wa *WhatsAppClient) Connect(ctx context.Context) { if err := wa.Main.updateProxy(ctx, wa.Client, false); err != nil { zerolog.Ctx(ctx).Err(err).Msg("Failed to update proxy") } + if ctx.Err() != nil { + return + } wa.startLoops() wa.Client.BackgroundEventCtx = wa.UserLogin.Log.WithContext(wa.Main.Bridge.BackgroundCtx) - if err := wa.Client.Connect(); err != nil { + if err := wa.Client.ConnectContext(ctx); err != nil { zerolog.Ctx(ctx).Err(err).Msg("Failed to connect to WhatsApp") state := status.BridgeState{ StateEvent: status.StateUnknownError, @@ -281,7 +284,7 @@ func (wa *WhatsAppClient) ConnectBackground(ctx context.Context, params *bridgev func (wa *WhatsAppClient) sendPNData(ctx context.Context, pn string) error { //lint:ignore SA1019 this is supposed to be dangerous - resp, err := wa.Client.DangerousInternals().SendIQ(whatsmeow.DangerousInfoQuery{ + resp, err := wa.Client.DangerousInternals().SendIQ(ctx, whatsmeow.DangerousInfoQuery{ Namespace: "urn:xmpp:whatsapp:push", Type: "get", To: types.ServerJID, @@ -289,7 +292,6 @@ func (wa *WhatsAppClient) sendPNData(ctx context.Context, pn string) error { Tag: "pn", Content: pn, }}, - Context: ctx, }) if err != nil { return fmt.Errorf("failed to send pn: %w", err) @@ -304,7 +306,7 @@ func (wa *WhatsAppClient) sendPNData(ctx context.Context, pn string) error { } zerolog.Ctx(ctx).Debug().Str("cat_data", string(catContentBytes)).Msg("Received cat response from sending pn data") //lint:ignore SA1019 this is supposed to be dangerous - err = wa.Client.DangerousInternals().SendNode(waBinary.Node{ + err = wa.Client.DangerousInternals().SendNode(ctx, waBinary.Node{ Tag: "ib", Content: []waBinary.Node{{ Tag: "cat", @@ -319,7 +321,7 @@ func (wa *WhatsAppClient) sendPNData(ctx context.Context, pn string) error { } func (wa *WhatsAppClient) startLoops() { - ctx, cancel := context.WithCancel(context.Background()) + ctx, cancel := context.WithCancel(wa.Main.Bridge.BackgroundCtx) oldStop := wa.stopLoops.Swap(&cancel) if oldStop != nil { (*oldStop)() @@ -409,7 +411,7 @@ func (wa *WhatsAppClient) HandleMatrixViewingChat(ctx context.Context, msg *brid } if wa.lastPresence != presence { - err := wa.updatePresence(presence) + err := wa.updatePresence(ctx, presence) if err != nil { zerolog.Ctx(ctx).Warn().Err(err).Msg("Failed to set presence when viewing chat") } @@ -444,8 +446,8 @@ func (wa *WhatsAppClient) HandleMatrixViewingChat(ctx context.Context, msg *brid return nil } -func (wa *WhatsAppClient) updatePresence(presence types.Presence) error { - err := wa.Client.SendPresence(presence) +func (wa *WhatsAppClient) updatePresence(ctx context.Context, presence types.Presence) error { + err := wa.Client.SendPresence(ctx, presence) if err == nil { wa.lastPresence = presence } diff --git a/pkg/connector/commands.go b/pkg/connector/commands.go index ec1b999..e86a6f9 100644 --- a/pkg/connector/commands.go +++ b/pkg/connector/commands.go @@ -65,7 +65,7 @@ func fnAccept(ce *commands.Event) { ce.Reply("Login not found") } else if !login.Client.IsLoggedIn() { ce.Reply("Not logged in") - } else if err = login.Client.(*WhatsAppClient).Client.JoinGroupWithInvite(meta.JID, meta.Inviter, meta.Code, meta.Expiration); err != nil { + } else if err = login.Client.(*WhatsAppClient).Client.JoinGroupWithInvite(ce.Ctx, meta.JID, meta.Inviter, meta.Code, meta.Expiration); err != nil { ce.Log.Err(err).Msg("Failed to accept group invite") ce.Reply("Failed to accept group invite: %v", err) } else { @@ -189,7 +189,7 @@ func fnInviteLink(ce *commands.Event) { ce.Reply("Can't get invite link to private chat") } else if portalJID.IsBroadcastList() { ce.Reply("Can't get invite link to broadcast list") - } else if link, err := wa.Client.GetGroupInviteLink(portalJID, reset); err != nil { + } else if link, err := wa.Client.GetGroupInviteLink(ce.Ctx, portalJID, reset); err != nil { ce.Reply("Failed to get invite link: %v", err) } else { ce.Reply(link) @@ -219,14 +219,14 @@ func fnResolveLink(ce *commands.Event) { } wa := login.Client.(*WhatsAppClient) if strings.HasPrefix(ce.Args[0], whatsmeow.InviteLinkPrefix) { - group, err := wa.Client.GetGroupInfoFromLink(ce.Args[0]) + group, err := wa.Client.GetGroupInfoFromLink(ce.Ctx, ce.Args[0]) if err != nil { ce.Reply("Failed to get group info: %v", err) return } ce.Reply("That invite link points at %s (`%s`)", group.Name, group.JID) } else if strings.HasPrefix(ce.Args[0], whatsmeow.BusinessMessageLinkPrefix) || strings.HasPrefix(ce.Args[0], whatsmeow.BusinessMessageLinkDirectPrefix) { - target, err := wa.Client.ResolveBusinessMessageLink(ce.Args[0]) + target, err := wa.Client.ResolveBusinessMessageLink(ce.Ctx, ce.Args[0]) if err != nil { ce.Reply("Failed to get business info: %v", err) return @@ -241,7 +241,7 @@ func fnResolveLink(ce *commands.Event) { } ce.Reply("That link points at %s (+%s).%s", target.PushName, target.JID.User, message) } else if strings.HasPrefix(ce.Args[0], whatsmeow.ContactQRLinkPrefix) || strings.HasPrefix(ce.Args[0], whatsmeow.ContactQRLinkDirectPrefix) { - target, err := wa.Client.ResolveContactQRLink(ce.Args[0]) + target, err := wa.Client.ResolveContactQRLink(ce.Ctx, ce.Args[0]) if err != nil { ce.Reply("Failed to get contact info: %v", err) return @@ -280,7 +280,7 @@ func fnJoin(ce *commands.Event) { wa := login.Client.(*WhatsAppClient) if strings.HasPrefix(ce.Args[0], whatsmeow.InviteLinkPrefix) { - jid, err := wa.Client.JoinGroupWithLink(ce.Args[0]) + jid, err := wa.Client.JoinGroupWithLink(ce.Ctx, ce.Args[0]) if err != nil { ce.Reply("Failed to join group: %v", err) return @@ -288,12 +288,12 @@ func fnJoin(ce *commands.Event) { ce.Log.Debug().Stringer("group_jid", jid).Msg("User successfully joined WhatsApp group with link") ce.Reply("Successfully joined group `%s`, the portal should be created momentarily", jid) } else if strings.HasPrefix(ce.Args[0], whatsmeow.NewsletterLinkPrefix) { - info, err := wa.Client.GetNewsletterInfoWithInvite(ce.Args[0]) + info, err := wa.Client.GetNewsletterInfoWithInvite(ce.Ctx, ce.Args[0]) if err != nil { ce.Reply("Failed to get channel info: %v", err) return } - err = wa.Client.FollowNewsletter(info.ID) + err = wa.Client.FollowNewsletter(ce.Ctx, info.ID) if err != nil { ce.Reply("Failed to follow channel: %v", err) return diff --git a/pkg/connector/directmedia.go b/pkg/connector/directmedia.go index ec1610c..c5d51ea 100644 --- a/pkg/connector/directmedia.go +++ b/pkg/connector/directmedia.go @@ -90,7 +90,7 @@ func (wa *WhatsAppConnector) downloadAvatarDirectMedia(ctx context.Context, pars zerolog.Ctx(ctx).Debug(). Str("avatar_id", parsedID.Avatar.AvatarID). Msg("Refreshing avatar URL from WhatsApp servers") - avatar, err := waClient.Client.GetProfilePictureInfo(parsedID.Avatar.TargetJID, &whatsmeow.GetProfilePictureParams{ + avatar, err := waClient.Client.GetProfilePictureInfo(ctx, parsedID.Avatar.TargetJID, &whatsmeow.GetProfilePictureParams{ IsCommunity: parsedID.Avatar.Community, }) if errors.Is(err, whatsmeow.ErrProfilePictureNotSet) || @@ -262,7 +262,7 @@ func (wa *WhatsAppClient) requestDirectMedia(ctx context.Context, rawMsgID netwo defer state.Unlock() if !state.requested { zerolog.Ctx(ctx).Debug().Msg("Sending request for missing media in direct download") - err := wa.sendMediaRequestDirect(rawMsgID, key) + err := wa.sendMediaRequestDirect(ctx, rawMsgID, key) if err != nil { return nil, fmt.Errorf("failed to send media retry request: %w", err) } diff --git a/pkg/connector/events.go b/pkg/connector/events.go index f371cc0..8791672 100644 --- a/pkg/connector/events.go +++ b/pkg/connector/events.go @@ -139,7 +139,7 @@ func (evt *WAMessageEvent) PreHandle(ctx context.Context, portal *bridgev2.Porta log := zerolog.Ctx(ctx).With().Str("action", "group lid migration").Logger() ctx = log.WithContext(ctx) meta.LIDMigrationAttempted = true - info, err := evt.wa.Client.GetGroupInfo(portalJID) + info, err := evt.wa.Client.GetGroupInfo(ctx, portalJID) if err != nil { log.Err(err).Msg("Failed to get group info for lid migration") return diff --git a/pkg/connector/handlematrix.go b/pkg/connector/handlematrix.go index fe77c37..38ea4af 100644 --- a/pkg/connector/handlematrix.go +++ b/pkg/connector/handlematrix.go @@ -322,7 +322,7 @@ func (wa *WhatsAppClient) HandleMatrixReadReceipt(ctx context.Context, receipt * messagesToRead[key] = append(messagesToRead[key], parsed.ID) } for messageSender, ids := range messagesToRead { - err = wa.Client.MarkRead(ids, receipt.Receipt.Timestamp, portalJID, messageSender) + err = wa.Client.MarkRead(ctx, ids, receipt.Receipt.Timestamp, portalJID, messageSender) if err != nil { log.Err(err).Strs("ids", ids).Msg("Failed to mark messages as read") } @@ -352,12 +352,12 @@ func (wa *WhatsAppClient) HandleMatrixTyping(ctx context.Context, msg *bridgev2. } if wa.Main.Config.SendPresenceOnTyping { - err = wa.updatePresence(types.PresenceAvailable) + err = wa.updatePresence(ctx, types.PresenceAvailable) if err != nil { zerolog.Ctx(ctx).Warn().Err(err).Msg("Failed to set presence on typing") } } - return wa.Client.SendChatPresence(portalJID, chatPresence, mediaPresence) + return wa.Client.SendChatPresence(ctx, portalJID, chatPresence, mediaPresence) } var errUnsupportedDisappearingTimer = bridgev2.WrapErrorInStatus(errors.New("invalid value for disappearing timer")).WithErrorAsMessage().WithIsCertain(true).WithSendNotice(true) @@ -375,7 +375,7 @@ func (wa *WhatsAppClient) HandleMatrixDisappearingTimer(ctx context.Context, msg } settingTS := time.UnixMilli(msg.Event.Timestamp) - err = wa.Client.SetDisappearingTimer(portalJID, msg.Content.Timer.Duration, settingTS) + err = wa.Client.SetDisappearingTimer(ctx, portalJID, msg.Content.Timer.Duration, settingTS) if err != nil { return false, err } @@ -430,7 +430,7 @@ func (wa *WhatsAppClient) HandleMatrixMembership(ctx context.Context, msg *bridg return false, fmt.Errorf("cannot get target intent: unknown type: %T", target) } - _, err = wa.Client.UpdateGroupParticipants(portalJID, changes, action) + _, err = wa.Client.UpdateGroupParticipants(ctx, portalJID, changes, action) if err != nil { return false, err } @@ -448,7 +448,7 @@ func (wa *WhatsAppClient) HandleMatrixRoomName(ctx context.Context, msg *bridgev return false, fmt.Errorf("cannot set room name for DM") } - err = wa.Client.SetGroupName(portalJID, msg.Content.Name) + err = wa.Client.SetGroupName(ctx, portalJID, msg.Content.Name) if err != nil { return false, err } @@ -471,7 +471,7 @@ func (wa *WhatsAppClient) HandleMatrixRoomTopic(ctx context.Context, msg *bridge newID := wa.Client.GenerateMessageID() oldID := msg.Portal.Metadata.(*waid.PortalMetadata).TopicID - err = wa.Client.SetGroupTopic(portalJID, oldID, newID, msg.Content.Topic) + err = wa.Client.SetGroupTopic(ctx, portalJID, oldID, newID, msg.Content.Topic) if err != nil { return false, err } @@ -506,7 +506,7 @@ func (wa *WhatsAppClient) HandleMatrixRoomAvatar(ctx context.Context, msg *bridg } } - avatarID, err := wa.Client.SetGroupPhoto(portalJID, data) + avatarID, err := wa.Client.SetGroupPhoto(ctx, portalJID, data) if err != nil { return false, err } diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index 4429d05..4a2c04e 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -129,7 +129,7 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) (success bool) { case *events.AppStateSyncComplete: if len(wa.GetStore().PushName) > 0 && evt.Name == appstate.WAPatchCriticalBlock { - err := wa.updatePresence(types.PresenceUnavailable) + err := wa.updatePresence(ctx, types.PresenceUnavailable) if err != nil { log.Warn().Err(err).Msg("Failed to send presence after app state sync") } @@ -142,7 +142,7 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) (success bool) { case *events.PushNameSetting: // Send presence available when connecting and when the pushname is changed. // This makes sure that outgoing messages always have the right pushname. - err := wa.updatePresence(types.PresenceUnavailable) + err := wa.updatePresence(ctx, types.PresenceUnavailable) if err != nil { log.Warn().Err(err).Msg("Failed to send presence after push name update") } @@ -163,12 +163,12 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) (success bool) { wa.UserLogin.BridgeState.Send(status.BridgeState{StateEvent: status.StateConnected}) if len(wa.GetStore().PushName) > 0 { go func() { - err := wa.updatePresence(types.PresenceUnavailable) + err := wa.updatePresence(ctx, types.PresenceUnavailable) if err != nil { log.Warn().Err(err).Msg("Failed to send initial presence after connecting") } }() - go wa.syncRemoteProfile(log.WithContext(context.Background()), nil) + go wa.syncRemoteProfile(ctx, nil) } case *events.OfflineSyncPreview: log.Info(). diff --git a/pkg/connector/login.go b/pkg/connector/login.go index 2442356..9614e33 100644 --- a/pkg/connector/login.go +++ b/pkg/connector/login.go @@ -352,7 +352,7 @@ func (wl *WALogin) Wait(ctx context.Context) (*bridgev2.LoginStep, error) { } ul.Client.(*WhatsAppClient).isNewLogin = true - ul.Client.Connect(ul.Log.WithContext(context.Background())) + ul.Client.Connect(ul.Log.WithContext(wl.Main.Bridge.BackgroundCtx)) return &bridgev2.LoginStep{ Type: bridgev2.LoginStepTypeComplete, diff --git a/pkg/connector/mediarequest.go b/pkg/connector/mediarequest.go index e0f4c5b..2c382d1 100644 --- a/pkg/connector/mediarequest.go +++ b/pkg/connector/mediarequest.go @@ -137,7 +137,7 @@ func (wa *WhatsAppClient) sendMediaRequest(ctx context.Context, req *wadb.MediaR req.Status = wadb.MediaBackfillRequestStatusRequestSkipped return } - err = wa.sendMediaRequestDirect(req.MessageID, req.MediaKey) + err = wa.sendMediaRequestDirect(ctx, req.MessageID, req.MediaKey) if err != nil { log.Err(err).Msg("Failed to send media retry request") req.Status = wadb.MediaBackfillRequestStatusRequestFailed @@ -148,12 +148,12 @@ func (wa *WhatsAppClient) sendMediaRequest(ctx context.Context, req *wadb.MediaR } } -func (wa *WhatsAppClient) sendMediaRequestDirect(rawMsgID networkid.MessageID, key []byte) error { +func (wa *WhatsAppClient) sendMediaRequestDirect(ctx context.Context, rawMsgID networkid.MessageID, key []byte) error { msgID, err := waid.ParseMessageID(rawMsgID) if err != nil { return fmt.Errorf("failed to parse message ID: %w", err) } - return wa.Client.SendMediaRetryReceipt(&types.MessageInfo{ + return wa.Client.SendMediaRetryReceipt(ctx, &types.MessageInfo{ ID: msgID.ID, MessageSource: types.MessageSource{ IsFromMe: msgID.Sender.User == wa.JID.User, diff --git a/pkg/connector/proxy.go b/pkg/connector/proxy.go index 770d66d..4ec202a 100644 --- a/pkg/connector/proxy.go +++ b/pkg/connector/proxy.go @@ -58,7 +58,10 @@ func (wa *WhatsAppConnector) updateProxy(ctx context.Context, client *whatsmeow. } if proxy, err := wa.getProxy(reason); err != nil { return fmt.Errorf("failed to get proxy address: %w", err) - } else if err = client.SetProxyAddress(proxy); err != nil { + } else if err = client.SetProxyAddress(proxy, whatsmeow.SetProxyOptions{ + OnlyLogin: wa.Config.ProxyOnlyLogin, + NoMedia: wa.Config.ProxyOnlyLogin, + }); err != nil { return fmt.Errorf("failed to set proxy address: %w", err) } zerolog.Ctx(ctx).Debug().Msg("Enabled proxy") diff --git a/pkg/connector/startchat.go b/pkg/connector/startchat.go index ee4ed9b..d11c830 100644 --- a/pkg/connector/startchat.go +++ b/pkg/connector/startchat.go @@ -62,7 +62,7 @@ func looksEmaily(str string) bool { return false } -func (wa *WhatsAppClient) validateIdentifer(number string) (types.JID, error) { +func (wa *WhatsAppClient) validateIdentifer(ctx context.Context, number string) (types.JID, error) { if strings.HasSuffix(number, "@"+types.BotServer) || strings.HasSuffix(number, "@"+types.HiddenUserServer) { return types.ParseJID(number) } else if strings.HasPrefix(number, waid.BotPrefix) || strings.HasPrefix(number, waid.LIDPrefix) { @@ -76,7 +76,7 @@ func (wa *WhatsAppClient) validateIdentifer(number string) (types.JID, error) { return types.EmptyJID, ErrInputLooksLikeEmail } else if wa.Client == nil || !wa.Client.IsLoggedIn() { return types.EmptyJID, bridgev2.ErrNotLoggedIn - } else if resp, err := wa.Client.IsOnWhatsApp([]string{number}); err != nil { + } else if resp, err := wa.Client.IsOnWhatsApp(ctx, []string{number}); err != nil { return types.EmptyJID, fmt.Errorf("failed to check if number is on WhatsApp: %w", err) } else if len(resp) == 0 { return types.EmptyJID, fmt.Errorf("the server did not respond to the query") @@ -144,7 +144,7 @@ func (wa *WhatsAppClient) CreateChatWithGhost(ctx context.Context, ghost *bridge } func (wa *WhatsAppClient) ResolveIdentifier(ctx context.Context, identifier string, startChat bool) (*bridgev2.ResolveIdentifierResponse, error) { - origJID, err := wa.validateIdentifer(identifier) + origJID, err := wa.validateIdentifer(ctx, identifier) if err != nil { return nil, err } @@ -318,7 +318,7 @@ func (wa *WhatsAppClient) CreateGroup(ctx context.Context, params *bridgev2.Grou } changed := false if avatarBytes != nil { - avatarID, err := wa.Client.SetGroupPhoto(resp.JID, avatarBytes) + avatarID, err := wa.Client.SetGroupPhoto(ctx, resp.JID, avatarBytes) if err != nil { zerolog.Ctx(ctx).Warn().Err(err).Msg("Failed to set group avatar after creating group") } else { @@ -335,7 +335,7 @@ func (wa *WhatsAppClient) CreateGroup(ctx context.Context, params *bridgev2.Grou } if params.Topic != nil { newTopicID := wa.Client.GenerateMessageID() - err = wa.Client.SetGroupTopic(resp.JID, "", newTopicID, params.Topic.Topic) + err = wa.Client.SetGroupTopic(ctx, resp.JID, "", newTopicID, params.Topic.Topic) if err != nil { zerolog.Ctx(ctx).Warn().Err(err).Msg("Failed to set group topic after creating group") } else { diff --git a/pkg/connector/userinfo.go b/pkg/connector/userinfo.go index 150a97d..5af3363 100644 --- a/pkg/connector/userinfo.go +++ b/pkg/connector/userinfo.go @@ -149,7 +149,7 @@ func (wa *WhatsAppClient) doGhostResync(ctx context.Context, queue map[types.JID return } log.Debug().Array("jids", exzerolog.ArrayOfStringers(ghostJIDs)).Msg("Doing background sync for users") - infos, err := wa.Client.GetUserInfo(ghostJIDs) + infos, err := wa.Client.GetUserInfo(ctx, ghostJIDs) if err != nil { log.Err(err).Msg("Failed to get user info for background sync") return @@ -302,7 +302,7 @@ func (wa *WhatsAppClient) fetchGhostAvatar(ctx context.Context, ghost *bridgev2. existingID = "" } var wrappedAvatar *bridgev2.Avatar - avatar, err := wa.Client.GetProfilePictureInfo(jid, &whatsmeow.GetProfilePictureParams{ExistingID: existingID}) + avatar, err := wa.Client.GetProfilePictureInfo(ctx, jid, &whatsmeow.GetProfilePictureParams{ExistingID: existingID}) if errors.Is(err, whatsmeow.ErrProfilePictureNotSet) { wrappedAvatar = &bridgev2.Avatar{ ID: "remove", From 759c05028b80c0f9dafb1fce5964514aa2716061 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 27 Oct 2025 17:46:54 +0200 Subject: [PATCH 031/153] msgconv/wa-media: reduce sticker size --- pkg/msgconv/wa-media.go | 43 +++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/pkg/msgconv/wa-media.go b/pkg/msgconv/wa-media.go index 647e98f..ed1dc8d 100644 --- a/pkg/msgconv/wa-media.go +++ b/pkg/msgconv/wa-media.go @@ -237,6 +237,8 @@ type MediaMessageWithDuration interface { GetSeconds() uint32 } +const WhatsAppStickerSize = 190 + func prepareMediaMessage(rawMsg MediaMessage) *PreparedMedia { extraInfo := map[string]any{} data := &PreparedMedia{ @@ -248,6 +250,22 @@ func prepareMediaMessage(rawMsg MediaMessage) *PreparedMedia { "info": extraInfo, }, } + if durationMsg, ok := rawMsg.(MediaMessageWithDuration); ok { + data.Info.Duration = int(durationMsg.GetSeconds() * 1000) + } + if dimensionMsg, ok := rawMsg.(MediaMessageWithDimensions); ok { + data.Info.Width = int(dimensionMsg.GetWidth()) + data.Info.Height = int(dimensionMsg.GetHeight()) + } + if captionMsg, ok := rawMsg.(MediaMessageWithCaption); ok && captionMsg.GetCaption() != "" { + data.Body = captionMsg.GetCaption() + } else { + data.Body = data.FileName + } + data.Info.Size = int(rawMsg.GetFileLength()) + data.Info.MimeType = rawMsg.GetMimetype() + data.ContextInfo = rawMsg.GetContextInfo() + switch msg := rawMsg.(type) { case *waE2E.ImageMessage: data.MsgType = event.MsgImage @@ -272,6 +290,16 @@ func prepareMediaMessage(rawMsg MediaMessage) *PreparedMedia { if msg.GetMimetype() == "application/was" && data.FileName == "sticker" { data.FileName = "sticker.json" } + if data.Info.Width == data.Info.Height { + data.Info.Width = WhatsAppStickerSize + data.Info.Height = WhatsAppStickerSize + } else if data.Info.Width > data.Info.Height { + data.Info.Height /= data.Info.Width / WhatsAppStickerSize + data.Info.Width = WhatsAppStickerSize + } else { + data.Info.Width /= data.Info.Height / WhatsAppStickerSize + data.Info.Height = WhatsAppStickerSize + } case *waE2E.VideoMessage: data.MsgType = event.MsgVideo if msg.GetGifPlayback() { @@ -285,22 +313,7 @@ func prepareMediaMessage(rawMsg MediaMessage) *PreparedMedia { default: panic(fmt.Errorf("unknown media message type %T", rawMsg)) } - if durationMsg, ok := rawMsg.(MediaMessageWithDuration); ok { - data.Info.Duration = int(durationMsg.GetSeconds() * 1000) - } - if dimensionMsg, ok := rawMsg.(MediaMessageWithDimensions); ok { - data.Info.Width = int(dimensionMsg.GetWidth()) - data.Info.Height = int(dimensionMsg.GetHeight()) - } - if captionMsg, ok := rawMsg.(MediaMessageWithCaption); ok && captionMsg.GetCaption() != "" { - data.Body = captionMsg.GetCaption() - } else { - data.Body = data.FileName - } - data.Info.Size = int(rawMsg.GetFileLength()) - data.Info.MimeType = rawMsg.GetMimetype() - data.ContextInfo = rawMsg.GetContextInfo() return data } From 11bd416e03a527747a97b63cb6e10f353f2d390e Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 27 Oct 2025 17:58:11 +0200 Subject: [PATCH 032/153] dependencies: update mautrix-go --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 971dfba..232d685 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( golang.org/x/sync v0.17.0 google.golang.org/protobuf v1.36.10 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.25.3-0.20251024094209-5d87d14b8858 + maunium.net/go/mautrix v0.25.3-0.20251025135936-d486dba9271c ) require ( diff --git a/go.sum b/go.sum index e991e5b..40979e3 100644 --- a/go.sum +++ b/go.sum @@ -111,5 +111,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.25.3-0.20251024094209-5d87d14b8858 h1:w4rgGYjb+lA/AIbyOaUAMDQ6KhxyrTxNxZMPljaXoYw= -maunium.net/go/mautrix v0.25.3-0.20251024094209-5d87d14b8858/go.mod h1:EWgYyp2iFZP7pnSm+rufHlO8YVnA2KnoNBDpwekiAwI= +maunium.net/go/mautrix v0.25.3-0.20251025135936-d486dba9271c h1:3mRJUCCMc9QDpXVZToqKuQNoCFABldGae0HFxiuCBzE= +maunium.net/go/mautrix v0.25.3-0.20251025135936-d486dba9271c/go.mod h1:EWgYyp2iFZP7pnSm+rufHlO8YVnA2KnoNBDpwekiAwI= From e1c3d5092f7e2b3e8151e9b27cf1e8af52c65a1d Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 27 Oct 2025 18:40:19 +0200 Subject: [PATCH 033/153] capabilities: advertise supported state events and member actions Closes #858 Closes #856 --- go.mod | 2 +- go.sum | 4 ++-- pkg/connector/capabilities.go | 25 +++++++++++++++++++++++-- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 232d685..2683e5b 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( golang.org/x/sync v0.17.0 google.golang.org/protobuf v1.36.10 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.25.3-0.20251025135936-d486dba9271c + maunium.net/go/mautrix v0.25.3-0.20251027163910-adc035b6a555 ) require ( diff --git a/go.sum b/go.sum index 40979e3..bf392fb 100644 --- a/go.sum +++ b/go.sum @@ -111,5 +111,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.25.3-0.20251025135936-d486dba9271c h1:3mRJUCCMc9QDpXVZToqKuQNoCFABldGae0HFxiuCBzE= -maunium.net/go/mautrix v0.25.3-0.20251025135936-d486dba9271c/go.mod h1:EWgYyp2iFZP7pnSm+rufHlO8YVnA2KnoNBDpwekiAwI= +maunium.net/go/mautrix v0.25.3-0.20251027163910-adc035b6a555 h1:6D/kRJyT+5+RRnJErNNglVwDMYU6EwQN0bRYqcdkfZw= +maunium.net/go/mautrix v0.25.3-0.20251027163910-adc035b6a555/go.mod h1:EWgYyp2iFZP7pnSm+rufHlO8YVnA2KnoNBDpwekiAwI= diff --git a/pkg/connector/capabilities.go b/pkg/connector/capabilities.go index 09307e2..75a47e9 100644 --- a/pkg/connector/capabilities.go +++ b/pkg/connector/capabilities.go @@ -8,6 +8,7 @@ import ( "go.mau.fi/util/jsontime" "go.mau.fi/util/ptr" "maunium.net/go/mautrix/bridgev2" + "maunium.net/go/mautrix/bridgev2/database" "maunium.net/go/mautrix/event" "go.mau.fi/mautrix-whatsapp/pkg/waid" @@ -50,7 +51,7 @@ func (wa *WhatsAppConnector) GetCapabilities() *bridgev2.NetworkGeneralCapabilit } func (wa *WhatsAppConnector) GetBridgeInfoVersion() (info, caps int) { - return 1, 5 + return 1, 6 } const WAMaxFileSize = 2000 * 1024 * 1024 @@ -65,7 +66,7 @@ func supportedIfFFmpeg() event.CapabilitySupportLevel { } func capID() string { - base := "fi.mau.whatsapp.capabilities.2025_10_07" + base := "fi.mau.whatsapp.capabilities.2025_10_27" if ffmpeg.Supported() { return base + "+ffmpeg" } @@ -161,6 +162,17 @@ var whatsappCaps = &event.RoomFeatures{ MaxSize: WAMaxFileSize, }, }, + State: event.StateFeatureMap{ + event.StateRoomName.Type: {Level: event.CapLevelFullySupported}, + event.StateRoomAvatar.Type: {Level: event.CapLevelFullySupported}, + event.StateTopic.Type: {Level: event.CapLevelFullySupported}, + event.StateBeeperDisappearingTimer.Type: {Level: event.CapLevelFullySupported}, + }, + MemberActions: event.MemberFeatureMap{ + event.MemberActionInvite: event.CapLevelFullySupported, + event.MemberActionKick: event.CapLevelFullySupported, + event.MemberActionLeave: event.CapLevelFullySupported, + }, MaxTextLength: MaxTextLength, LocationMessage: event.CapLevelFullySupported, Poll: event.CapLevelFullySupported, @@ -179,9 +191,16 @@ var whatsappCaps = &event.RoomFeatures{ DeleteChat: true, } +var whatsappDMCaps *event.RoomFeatures var whatsappCAGCaps *event.RoomFeatures func init() { + whatsappDMCaps = ptr.Clone(whatsappCaps) + whatsappDMCaps.ID = capID() + "+dm" + whatsappDMCaps.State = event.StateFeatureMap{ + event.StateBeeperDisappearingTimer.Type: {Level: event.CapLevelFullySupported}, + } + whatsappDMCaps.MemberActions = nil whatsappCAGCaps = ptr.Clone(whatsappCaps) whatsappCAGCaps.ID = capID() + "+cag" whatsappCAGCaps.Reply = event.CapLevelUnsupported @@ -191,6 +210,8 @@ func init() { func (wa *WhatsAppClient) GetCapabilities(ctx context.Context, portal *bridgev2.Portal) *event.RoomFeatures { if portal.Metadata.(*waid.PortalMetadata).CommunityAnnouncementGroup { return whatsappCAGCaps + } else if portal.RoomType == database.RoomTypeDM { + return whatsappDMCaps } return whatsappCaps } From ddc6f504a5369e989a7d81dfcfd45b8703764378 Mon Sep 17 00:00:00 2001 From: Vaibhav Raj <68665948+dead8309@users.noreply.github.com> Date: Mon, 27 Oct 2025 22:39:06 +0530 Subject: [PATCH 034/153] login: wrap ratelimit errors from whatsapp (#852) --- pkg/connector/login.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pkg/connector/login.go b/pkg/connector/login.go index 9614e33..88e7b75 100644 --- a/pkg/connector/login.go +++ b/pkg/connector/login.go @@ -74,6 +74,11 @@ var ( Err: "Phone number must be in international format", StatusCode: http.StatusBadRequest, } + ErrRateLimitedByWhatsApp = bridgev2.RespError{ + ErrCode: "FI.MAU.WHATSAPP.RATE_LIMITED", + Err: "Rate limited by WhatsApp", + StatusCode: http.StatusTooManyRequests, + } ) func (wa *WhatsAppConnector) CreateLogin(_ context.Context, user *bridgev2.User, flowID string) (bridgev2.LoginProcess, error) { @@ -196,6 +201,8 @@ func (wl *WALogin) SubmitUserInput(ctx context.Context, input map[string]string) return nil, ErrPhoneNumberTooShort } else if errors.Is(err, whatsmeow.ErrPhoneNumberIsNotInternational) { return nil, ErrPhoneNumberIsNotInternational + } else if errors.Is(err, whatsmeow.ErrIQRateOverLimit) { + return nil, ErrRateLimitedByWhatsApp } return nil, err } From 1b24b61c0ae22e2dada56877e1eb06ede6d240fa Mon Sep 17 00:00:00 2001 From: Rajeh Taher Date: Tue, 28 Oct 2025 15:17:51 +0200 Subject: [PATCH 035/153] msgconv,handewhatsapp: Bridge "Live photos" as GIFs (#845) Co-authored-by: Tulir Asokan --- pkg/connector/handlewhatsapp.go | 2 ++ pkg/msgconv/wa-media.go | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index 4a2c04e..17324cc 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -316,6 +316,8 @@ func (wa *WhatsAppClient) handleWAMessage(ctx context.Context, evt *events.Messa evt.Message = &waE2E.Message{ ProtocolMessage: protocolMsg, } + } else if assocType == waE2E.MessageAssociation_MOTION_PHOTO { + evt.Message = evt.Message.GetAssociatedChildMessage().GetMessage() } parsedMessageType := getMessageType(evt.Message) diff --git a/pkg/msgconv/wa-media.go b/pkg/msgconv/wa-media.go index ed1dc8d..9a1ceb3 100644 --- a/pkg/msgconv/wa-media.go +++ b/pkg/msgconv/wa-media.go @@ -302,7 +302,8 @@ func prepareMediaMessage(rawMsg MediaMessage) *PreparedMedia { } case *waE2E.VideoMessage: data.MsgType = event.MsgVideo - if msg.GetGifPlayback() { + pairedMediaType := msg.GetContextInfo().GetPairedMediaType() + if msg.GetGifPlayback() || pairedMediaType == waE2E.ContextInfo_MOTION_PHOTO_PARENT || pairedMediaType == waE2E.ContextInfo_MOTION_PHOTO_CHILD { extraInfo["fi.mau.gif"] = true extraInfo["fi.mau.loop"] = true extraInfo["fi.mau.autoplay"] = true From f57f78b705670ad2ddb51ba2ca1d05efff2ae1e7 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 28 Oct 2025 15:09:42 +0200 Subject: [PATCH 036/153] dependencies: update mautrix-go --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 2683e5b..0f59234 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( golang.org/x/sync v0.17.0 google.golang.org/protobuf v1.36.10 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.25.3-0.20251027163910-adc035b6a555 + maunium.net/go/mautrix v0.25.3-0.20251028130646-bea28c1381cd ) require ( diff --git a/go.sum b/go.sum index bf392fb..14068a9 100644 --- a/go.sum +++ b/go.sum @@ -111,5 +111,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.25.3-0.20251027163910-adc035b6a555 h1:6D/kRJyT+5+RRnJErNNglVwDMYU6EwQN0bRYqcdkfZw= -maunium.net/go/mautrix v0.25.3-0.20251027163910-adc035b6a555/go.mod h1:EWgYyp2iFZP7pnSm+rufHlO8YVnA2KnoNBDpwekiAwI= +maunium.net/go/mautrix v0.25.3-0.20251028130646-bea28c1381cd h1:4OfgnwTd71vgHGc+kBwhWsb92ePZVvsDbyTLJiy+PKU= +maunium.net/go/mautrix v0.25.3-0.20251028130646-bea28c1381cd/go.mod h1:EWgYyp2iFZP7pnSm+rufHlO8YVnA2KnoNBDpwekiAwI= From 3531a96bd47390b929fd836f2053d03e810d22db Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 28 Oct 2025 17:08:13 +0200 Subject: [PATCH 037/153] userinfo: always fetch alt jid contact when updating user info --- go.mod | 2 +- go.sum | 4 +-- pkg/connector/handlewhatsapp.go | 4 +++ pkg/connector/userinfo.go | 43 +++++++++++++++++++-------------- 4 files changed, 32 insertions(+), 21 deletions(-) diff --git a/go.mod b/go.mod index 0f59234..2e10080 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.2 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20251027141726-3d82d3101dd1 + go.mau.fi/whatsmeow v0.0.0-20251028144453-ba0d4eb09a00 golang.org/x/image v0.32.0 golang.org/x/net v0.46.0 golang.org/x/sync v0.17.0 diff --git a/go.sum b/go.sum index 14068a9..577ac96 100644 --- a/go.sum +++ b/go.sum @@ -79,8 +79,8 @@ go.mau.fi/util v0.9.2 h1:+S4Z03iCsGqU2WY8X2gySFsFjaLlUHFRDVCYvVwynKM= go.mau.fi/util v0.9.2/go.mod h1:055elBBCJSdhRsmub7ci9hXZPgGr1U6dYg44cSgRgoU= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20251027141726-3d82d3101dd1 h1:xjTxq7OfIV+oVJnsWrXX5lrIt8cSMV6yHppdp2+fVQE= -go.mau.fi/whatsmeow v0.0.0-20251027141726-3d82d3101dd1/go.mod h1:RwBrMQAWCHGzMdDZ6EwjcY4Aj3g8Efx8c7GACTdiAME= +go.mau.fi/whatsmeow v0.0.0-20251028144453-ba0d4eb09a00 h1:dbr9L/PbazTXwuKyrwQepy6+NpDBd3GhrpmTWfWIZSo= +go.mau.fi/whatsmeow v0.0.0-20251028144453-ba0d4eb09a00/go.mod h1:RwBrMQAWCHGzMdDZ6EwjcY4Aj3g8Efx8c7GACTdiAME= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index 17324cc..5f71a64 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -150,6 +150,10 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) (success bool) { if err != nil { log.Err(err).Msg("Failed to update push name in store") } + _, _, err = wa.GetStore().Contacts.PutPushName(ctx, wa.GetStore().GetLID().ToNonAD(), evt.Action.GetName()) + if err != nil { + log.Err(err).Msg("Failed to update push name in store") + } go wa.syncGhost(wa.JID.ToNonAD(), "push name setting", nil) case *events.Contact: go wa.syncGhost(evt.JID, "contact event", nil) diff --git a/pkg/connector/userinfo.go b/pkg/connector/userinfo.go index 5af3363..6fbb565 100644 --- a/pkg/connector/userinfo.go +++ b/pkg/connector/userinfo.go @@ -194,29 +194,30 @@ func (wa *WhatsAppClient) contactToUserInfo(ctx context.Context, jid types.JID, } else if jid == types.LegacyPSAJID || jid == types.PSAJID { contact.PushName = "WhatsApp" } - var phone string - if jid.Server == types.DefaultUserServer { - phone = "+" + jid.User - } else if jid.Server == types.HiddenUserServer { - pnJID, err := wa.GetStore().LIDs.GetPNForLID(ctx, jid) + var altJID types.JID + if jid.Server == types.DefaultUserServer || jid.Server == types.HiddenUserServer { + var err error + altJID, err = wa.GetStore().GetAltJID(ctx, jid) if err != nil { - zerolog.Ctx(ctx).Err(err).Stringer("lid", jid).Msg("Failed to get PN for LID") - } else if pnJID.IsEmpty() { - zerolog.Ctx(ctx).Debug().Stringer("lid", jid).Msg("Phone number not found for LID in contactToUserInfo") + zerolog.Ctx(ctx).Err(err).Stringer("source_jid", jid).Msg("Failed to get alt JID") + } else if altJID.IsEmpty() { + zerolog.Ctx(ctx).Debug().Stringer("source_jid", jid).Msg("Alternate JID not found in contactToUserInfo") } else { - phone = "+" + pnJID.User - extraContact, err := wa.GetStore().Contacts.GetContact(ctx, pnJID) + extraContact, err := wa.GetStore().Contacts.GetContact(ctx, altJID) if err != nil { zerolog.Ctx(ctx).Err(err). - Stringer("lid", jid). - Stringer("pn_jid", pnJID). - Msg("Failed to get contact info from PN") + Stringer("source_jid", jid). + Stringer("alt_jid", altJID). + Msg("Failed to get contact info from alternate JID") } else { - if contact.FirstName == "" { - contact.FirstName = extraContact.FirstName - } - if contact.FullName == "" { - contact.FullName = extraContact.FullName + // Phone contact info should only be stored for phone number JIDs + if altJID.Server == types.DefaultUserServer { + if contact.FirstName == "" { + contact.FirstName = extraContact.FirstName + } + if contact.FullName == "" { + contact.FullName = extraContact.FullName + } } if contact.PushName == "" { contact.PushName = extraContact.PushName @@ -227,6 +228,12 @@ func (wa *WhatsAppClient) contactToUserInfo(ctx context.Context, jid types.JID, } } } + var phone string + if jid.Server == types.DefaultUserServer { + phone = "+" + jid.User + } else if altJID.Server == types.DefaultUserServer { + phone = "+" + altJID.User + } ui := &bridgev2.UserInfo{ Name: ptr.Ptr(wa.Main.Config.FormatDisplayname(jid, phone, contact)), IsBot: ptr.Ptr(jid.IsBot()), From 1f65164cf32a20a61dc21ecf47ef023350fb2366 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 28 Oct 2025 18:14:23 +0200 Subject: [PATCH 038/153] userinfo: add debug log if lid and pn contact info don't agree --- pkg/connector/userinfo.go | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/pkg/connector/userinfo.go b/pkg/connector/userinfo.go index 6fbb565..aa84938 100644 --- a/pkg/connector/userinfo.go +++ b/pkg/connector/userinfo.go @@ -225,6 +225,28 @@ func (wa *WhatsAppClient) contactToUserInfo(ctx context.Context, jid types.JID, if contact.BusinessName == "" { contact.BusinessName = extraContact.BusinessName } + if contact.PushName != "" && extraContact.PushName != "" && contact.PushName != extraContact.PushName { + zerolog.Ctx(ctx).Debug(). + Stringer("source_jid", jid). + Stringer("alt_jid", altJID). + Str("source_push_name", contact.PushName). + Str("alt_push_name", extraContact.PushName). + Msg("Conflicting push names between JIDs") + if altJID.Server == types.DefaultUserServer { + contact.PushName = extraContact.PushName + } + } + if contact.BusinessName != "" && extraContact.BusinessName != "" && contact.BusinessName != extraContact.BusinessName { + zerolog.Ctx(ctx).Debug(). + Stringer("source_jid", jid). + Stringer("alt_jid", altJID). + Str("source_push_name", contact.BusinessName). + Str("alt_push_name", extraContact.BusinessName). + Msg("Conflicting business names between JIDs") + if altJID.Server == types.DefaultUserServer { + contact.BusinessName = extraContact.BusinessName + } + } } } } From 040456f34621f740baa654a575a9f12a86f19398 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 28 Oct 2025 18:51:55 +0200 Subject: [PATCH 039/153] dependencies: update whatsmeow --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 2e10080..7039778 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.2 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20251028144453-ba0d4eb09a00 + go.mau.fi/whatsmeow v0.0.0-20251028165006-ad7a618ba42f golang.org/x/image v0.32.0 golang.org/x/net v0.46.0 golang.org/x/sync v0.17.0 diff --git a/go.sum b/go.sum index 577ac96..7ca494b 100644 --- a/go.sum +++ b/go.sum @@ -79,8 +79,8 @@ go.mau.fi/util v0.9.2 h1:+S4Z03iCsGqU2WY8X2gySFsFjaLlUHFRDVCYvVwynKM= go.mau.fi/util v0.9.2/go.mod h1:055elBBCJSdhRsmub7ci9hXZPgGr1U6dYg44cSgRgoU= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20251028144453-ba0d4eb09a00 h1:dbr9L/PbazTXwuKyrwQepy6+NpDBd3GhrpmTWfWIZSo= -go.mau.fi/whatsmeow v0.0.0-20251028144453-ba0d4eb09a00/go.mod h1:RwBrMQAWCHGzMdDZ6EwjcY4Aj3g8Efx8c7GACTdiAME= +go.mau.fi/whatsmeow v0.0.0-20251028165006-ad7a618ba42f h1:UfzKgeEBRlDj3E2B/z+no17BstkAxO4kIUNSgR6Cwrw= +go.mau.fi/whatsmeow v0.0.0-20251028165006-ad7a618ba42f/go.mod h1:RwBrMQAWCHGzMdDZ6EwjcY4Aj3g8Efx8c7GACTdiAME= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= From 25ea0d986a0b3d6cc4eb77228d31ed438bdfb0b1 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 29 Oct 2025 14:53:40 +0200 Subject: [PATCH 040/153] handlewhatsapp: ignore motion photos for now --- pkg/connector/handlewhatsapp.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index 5f71a64..621caf0 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -321,7 +321,12 @@ func (wa *WhatsAppClient) handleWAMessage(ctx context.Context, evt *events.Messa ProtocolMessage: protocolMsg, } } else if assocType == waE2E.MessageAssociation_MOTION_PHOTO { - evt.Message = evt.Message.GetAssociatedChildMessage().GetMessage() + //evt.Message = evt.Message.GetAssociatedChildMessage().GetMessage() + wa.UserLogin.Log.Debug(). + Str("message_id", evt.Info.ID). + Str("parent_id", messageAssoc.GetParentMessageKey().GetID()). + Msg("Ignoring motion photo update") + return } parsedMessageType := getMessageType(evt.Message) From 22e60277763a4dc8911ee8b138ef86080dc70270 Mon Sep 17 00:00:00 2001 From: Conan Date: Fri, 31 Oct 2025 00:13:03 +0800 Subject: [PATCH 041/153] startchat: normalize identifiers to phone numbers when creating group (#855) --- pkg/connector/startchat.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pkg/connector/startchat.go b/pkg/connector/startchat.go index d11c830..22d4ad8 100644 --- a/pkg/connector/startchat.go +++ b/pkg/connector/startchat.go @@ -217,7 +217,13 @@ func (wa *WhatsAppClient) CreateGroup(ctx context.Context, params *bridgev2.Grou CreateKey: createKey, } for i, participant := range params.Participants { - req.Participants[i] = waid.ParseUserID(participant) + jid := waid.ParseUserID(participant) + // Normalize to PN if it's a LID + jid, err := wa.startChatLIDToPN(ctx, jid) + if err != nil { + return nil, fmt.Errorf("failed to normalize participant %s: %w", participant, err) + } + req.Participants[i] = jid } if params.Parent != nil { var err error From e4ea06f68eff8e8e142b59e7238930bb12399a11 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 6 Nov 2025 17:38:05 +0100 Subject: [PATCH 042/153] dependencies: update whatsmeow --- go.mod | 2 +- go.sum | 4 ++-- pkg/connector/connector.go | 9 +++++---- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 7039778..37fbaef 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.2 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20251028165006-ad7a618ba42f + go.mau.fi/whatsmeow v0.0.0-20251106163046-720bd0b4a715 golang.org/x/image v0.32.0 golang.org/x/net v0.46.0 golang.org/x/sync v0.17.0 diff --git a/go.sum b/go.sum index 7ca494b..9b8920a 100644 --- a/go.sum +++ b/go.sum @@ -79,8 +79,8 @@ go.mau.fi/util v0.9.2 h1:+S4Z03iCsGqU2WY8X2gySFsFjaLlUHFRDVCYvVwynKM= go.mau.fi/util v0.9.2/go.mod h1:055elBBCJSdhRsmub7ci9hXZPgGr1U6dYg44cSgRgoU= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20251028165006-ad7a618ba42f h1:UfzKgeEBRlDj3E2B/z+no17BstkAxO4kIUNSgR6Cwrw= -go.mau.fi/whatsmeow v0.0.0-20251028165006-ad7a618ba42f/go.mod h1:RwBrMQAWCHGzMdDZ6EwjcY4Aj3g8Efx8c7GACTdiAME= +go.mau.fi/whatsmeow v0.0.0-20251106163046-720bd0b4a715 h1:JxVirSDFmhhDEv3LmXW8ybwHAKV51Izz3burUg81w2o= +go.mau.fi/whatsmeow v0.0.0-20251106163046-720bd0b4a715/go.mod h1:RwBrMQAWCHGzMdDZ6EwjcY4Aj3g8Efx8c7GACTdiAME= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= diff --git a/pkg/connector/connector.go b/pkg/connector/connector.go index 4e50ca5..25a0707 100644 --- a/pkg/connector/connector.go +++ b/pkg/connector/connector.go @@ -120,11 +120,12 @@ func (wa *WhatsAppConnector) Init(bridge *bridgev2.Bridge) { store.DeviceProps.Os = proto.String(wa.Config.OSName) store.DeviceProps.RequireFullSync = proto.Bool(wa.Config.HistorySync.RequestFullSync) if fsc := wa.Config.HistorySync.FullSyncConfig; fsc.DaysLimit > 0 && fsc.SizeLimit > 0 && fsc.StorageQuota > 0 { - store.DeviceProps.HistorySyncConfig = &waCompanionReg.DeviceProps_HistorySyncConfig{ - FullSyncDaysLimit: proto.Uint32(fsc.DaysLimit), - FullSyncSizeMbLimit: proto.Uint32(fsc.SizeLimit), - StorageQuotaMb: proto.Uint32(fsc.StorageQuota), + if store.DeviceProps.HistorySyncConfig == nil { + store.DeviceProps.HistorySyncConfig = &waCompanionReg.DeviceProps_HistorySyncConfig{} } + store.DeviceProps.HistorySyncConfig.FullSyncDaysLimit = proto.Uint32(fsc.DaysLimit) + store.DeviceProps.HistorySyncConfig.FullSyncSizeMbLimit = proto.Uint32(fsc.SizeLimit) + store.DeviceProps.HistorySyncConfig.StorageQuotaMb = proto.Uint32(fsc.StorageQuota) } platformID, ok := waCompanionReg.DeviceProps_PlatformType_value[strings.ToUpper(wa.Config.BrowserName)] if ok { From cba1704046a7842f9833004b86b7d831cf719144 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sat, 8 Nov 2025 23:19:12 +0200 Subject: [PATCH 043/153] handlewhatsapp: reroute LID typing notifications and fix receipts Fixes #860 --- pkg/connector/handlewhatsapp.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index 621caf0..9bac6ad 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -399,6 +399,9 @@ func (wa *WhatsAppClient) handleWAUndecryptableMessage(ctx context.Context, evt } func (wa *WhatsAppClient) handleWAReceipt(ctx context.Context, evt *events.Receipt) (success bool) { + if evt.Chat.Server == types.HiddenUserServer && evt.SenderAlt.IsEmpty() { + evt.SenderAlt, _ = wa.GetStore().LIDs.GetPNForLID(ctx, evt.Sender) + } if evt.Chat.Server == types.HiddenUserServer && evt.Sender.ToNonAD() == evt.Chat && evt.SenderAlt.Server == types.DefaultUserServer { wa.UserLogin.Log.Debug(). Stringer("lid", evt.Sender). @@ -455,6 +458,15 @@ func (wa *WhatsAppClient) handleWAReceipt(ctx context.Context, evt *events.Recei } func (wa *WhatsAppClient) handleWAChatPresence(ctx context.Context, evt *events.ChatPresence) { + if evt.Chat.Server == types.HiddenUserServer && evt.Sender.ToNonAD() == evt.Chat { + if evt.SenderAlt.IsEmpty() { + evt.SenderAlt, _ = wa.GetStore().LIDs.GetPNForLID(ctx, evt.Sender) + } + if evt.SenderAlt.Server == types.DefaultUserServer { + evt.Sender, evt.SenderAlt = evt.SenderAlt, evt.Sender + evt.Chat = evt.Sender.ToNonAD() + } + } typingType := bridgev2.TypingTypeText timeout := 15 * time.Second if evt.Media == types.ChatPresenceMediaAudio { From 3b59a4627723fbc087d7c450c7c3466b26b2521e Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 9 Nov 2025 11:27:39 +0200 Subject: [PATCH 044/153] msgconv/from-whatsapp: reroute DM reply participants to phone numbers --- pkg/msgconv/from-whatsapp.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pkg/msgconv/from-whatsapp.go b/pkg/msgconv/from-whatsapp.go index c4be4a4..80b92ed 100644 --- a/pkg/msgconv/from-whatsapp.go +++ b/pkg/msgconv/from-whatsapp.go @@ -257,6 +257,13 @@ func (mc *MessageConverter) ToMatrix( if chat.IsEmpty() { chat, _ = waid.ParsePortalID(portal.ID) } + // We reroute all DMs to the phone number JID, so reroute reply participants too + if store := getClient(ctx).Store; store != nil && chat.Server == types.DefaultUserServer { + pcpPN, _ := store.LIDs.GetPNForLID(ctx, pcp) + if !pcpPN.IsEmpty() { + pcp = pcpPN + } + } cm.ReplyTo = &networkid.MessageOptionalPartID{ MessageID: waid.MakeMessageID(chat, pcp, contextInfo.GetStanzaID()), } From ec1229a4cb3e3402736f84e8d6162ab046d3f1fe Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 9 Nov 2025 11:41:37 +0200 Subject: [PATCH 045/153] dependencies: update mautrix-go --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 37fbaef..bb2aa34 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( golang.org/x/sync v0.17.0 google.golang.org/protobuf v1.36.10 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.25.3-0.20251028130646-bea28c1381cd + maunium.net/go/mautrix v0.25.3-0.20251109094010-14e16a3a8190 ) require ( diff --git a/go.sum b/go.sum index 9b8920a..6bdffa0 100644 --- a/go.sum +++ b/go.sum @@ -111,5 +111,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.25.3-0.20251028130646-bea28c1381cd h1:4OfgnwTd71vgHGc+kBwhWsb92ePZVvsDbyTLJiy+PKU= -maunium.net/go/mautrix v0.25.3-0.20251028130646-bea28c1381cd/go.mod h1:EWgYyp2iFZP7pnSm+rufHlO8YVnA2KnoNBDpwekiAwI= +maunium.net/go/mautrix v0.25.3-0.20251109094010-14e16a3a8190 h1:5v25ZS99ilLXHjYX3uMiyHGBNhFPXgOgGfr9GkakRTg= +maunium.net/go/mautrix v0.25.3-0.20251109094010-14e16a3a8190/go.mod h1:EWgYyp2iFZP7pnSm+rufHlO8YVnA2KnoNBDpwekiAwI= From b4f3a1f49233abb7c6e81a6be465ea952c67483e Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 9 Nov 2025 13:35:51 +0200 Subject: [PATCH 046/153] msgconv/from-whatsapp: update reply participant rerouting --- pkg/msgconv/from-whatsapp.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/pkg/msgconv/from-whatsapp.go b/pkg/msgconv/from-whatsapp.go index 80b92ed..a1156bc 100644 --- a/pkg/msgconv/from-whatsapp.go +++ b/pkg/msgconv/from-whatsapp.go @@ -258,11 +258,24 @@ func (mc *MessageConverter) ToMatrix( chat, _ = waid.ParsePortalID(portal.ID) } // We reroute all DMs to the phone number JID, so reroute reply participants too - if store := getClient(ctx).Store; store != nil && chat.Server == types.DefaultUserServer { + if store := getClient(ctx).Store; store != nil && chat.Server == types.DefaultUserServer && pcp.Server == types.HiddenUserServer { pcpPN, _ := store.LIDs.GetPNForLID(ctx, pcp) + zerolog.Ctx(ctx).Debug(). + Stringer("orig_participant", pcp). + Stringer("rerouted_participant", pcpPN). + Msg("Rerouting reply target (PN recipient in LID DM)") if !pcpPN.IsEmpty() { pcp = pcpPN } + } 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 + } } cm.ReplyTo = &networkid.MessageOptionalPartID{ MessageID: waid.MakeMessageID(chat, pcp, contextInfo.GetStanzaID()), From 5ef54de8f04cb70b7192eeafd150120b26c39c2f Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 10 Nov 2025 13:08:50 +0200 Subject: [PATCH 047/153] dependencies: update whatsmeow --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index bb2aa34..de830cc 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.2 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20251106163046-720bd0b4a715 + go.mau.fi/whatsmeow v0.0.0-20251110110826-a121e2b9cd1e golang.org/x/image v0.32.0 golang.org/x/net v0.46.0 golang.org/x/sync v0.17.0 diff --git a/go.sum b/go.sum index 6bdffa0..992979e 100644 --- a/go.sum +++ b/go.sum @@ -79,8 +79,8 @@ go.mau.fi/util v0.9.2 h1:+S4Z03iCsGqU2WY8X2gySFsFjaLlUHFRDVCYvVwynKM= go.mau.fi/util v0.9.2/go.mod h1:055elBBCJSdhRsmub7ci9hXZPgGr1U6dYg44cSgRgoU= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20251106163046-720bd0b4a715 h1:JxVirSDFmhhDEv3LmXW8ybwHAKV51Izz3burUg81w2o= -go.mau.fi/whatsmeow v0.0.0-20251106163046-720bd0b4a715/go.mod h1:RwBrMQAWCHGzMdDZ6EwjcY4Aj3g8Efx8c7GACTdiAME= +go.mau.fi/whatsmeow v0.0.0-20251110110826-a121e2b9cd1e h1:I93PJ14VjWNTJ2BVm2jT9WQ60fMDNmGng6qZs2CzYpg= +go.mau.fi/whatsmeow v0.0.0-20251110110826-a121e2b9cd1e/go.mod h1:RwBrMQAWCHGzMdDZ6EwjcY4Aj3g8Efx8c7GACTdiAME= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= From 6be815e6f584ab73469e6a35be90178b89439f8a Mon Sep 17 00:00:00 2001 From: Nick Mills-Barrett Date: Fri, 31 Oct 2025 12:03:12 +0000 Subject: [PATCH 048/153] connector: re-request media when no URL present in current message --- pkg/connector/directmedia.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/connector/directmedia.go b/pkg/connector/directmedia.go index c5d51ea..2b289b8 100644 --- a/pkg/connector/directmedia.go +++ b/pkg/connector/directmedia.go @@ -170,7 +170,7 @@ func (wa *WhatsAppConnector) downloadMessageDirectMedia(ctx context.Context, par return &mediaproxy.GetMediaResponseFile{ Callback: func(f *os.File) error { err := waClient.Client.DownloadToFile(ctx, keys, f) - if errors.Is(err, whatsmeow.ErrMediaDownloadFailedWith403) || errors.Is(err, whatsmeow.ErrMediaDownloadFailedWith404) || errors.Is(err, whatsmeow.ErrMediaDownloadFailedWith410) { + if errors.Is(err, whatsmeow.ErrMediaDownloadFailedWith403) || errors.Is(err, whatsmeow.ErrMediaDownloadFailedWith404) || errors.Is(err, whatsmeow.ErrMediaDownloadFailedWith410) || errors.Is(err, whatsmeow.ErrNoURLPresent) { val := params["fi.mau.whatsapp.reload_media"] if val == "false" || (!wa.Config.DirectMediaAutoRequest && val != "true") { return ErrReloadNeeded From 69622b6ee75b2a40f424433737437001d81999dd Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 11 Nov 2025 01:45:47 +0200 Subject: [PATCH 049/153] changelog: update --- CHANGELOG.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e65fef..b1a81a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,24 @@ +# v25.11 (unreleased) + +* Added interface support for notifying about failed invites when creating a + group and sending the invites via DM (only applicable to provisioning API). +* Changed contact list API to only include actual phone contacts. +* Removed extra unrecognized message notice when receiving live photos + (bridging the live photo video is not currently planned). +* Fixed pairing not working with latest WhatsApp Android version. +* Fixed replies, read receipts and typing notifications not being bridged + correctly after DM LID migration. +* Fixed some cases of LID and phone number user infos getting out of sync. +* Fixed muting chat forever not being bridged correctly from WhatsApp. +* Fixed space not being deleted when leaving community on WhatsApp. +* Fixed sticker size metadata on Matrix not matching how native WhatsApp Web + renders them. +* Fixed ratelimit errors in login not being exposed to the user properly + (thanks to [@dead8309] in [#852]). + +[@dead8309]: https://github.com/dead8309 +[#852]: https://github.com/mautrix/whatsapp/pull/852 + # v25.10 * Switched to calendar versioning. From ba9f5c61c19f022cd62387c328684c8ab0721fc1 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 11 Nov 2025 18:22:25 +0200 Subject: [PATCH 050/153] handle*: fix more DM LID rerouting issues --- pkg/connector/events.go | 10 +++++-- pkg/connector/handlematrix.go | 2 +- pkg/connector/handlewhatsapp.go | 48 +++++++++++++++------------------ pkg/msgconv/from-whatsapp.go | 1 + pkg/msgconv/wa-poll.go | 48 ++++++++++++++++++++++++++++----- 5 files changed, 73 insertions(+), 36 deletions(-) diff --git a/pkg/connector/events.go b/pkg/connector/events.go index 8791672..35fded4 100644 --- a/pkg/connector/events.go +++ b/pkg/connector/events.go @@ -209,9 +209,15 @@ func (evt *WAMessageEvent) ConvertEdit(ctx context.Context, portal *bridgev2.Por func (evt *WAMessageEvent) GetTargetMessage() networkid.MessageID { if reactionMsg := evt.Message.GetReactionMessage(); reactionMsg != nil { - return msgconv.KeyToMessageID(evt.wa.Client, evt.Info.Chat, evt.Info.Sender, reactionMsg.GetKey()) + ctx := evt.wa.UserLogin.Log. + With().Str("action", "get reaction target message").Str("message_id", evt.Info.ID).Logger(). + WithContext(evt.wa.Main.Bridge.BackgroundCtx) + return msgconv.KeyToMessageID(ctx, evt.wa.Client, evt.Info.Chat, evt.Info.Sender, reactionMsg.GetKey()) } else if protocolMsg := evt.Message.GetProtocolMessage(); protocolMsg != nil { - return msgconv.KeyToMessageID(evt.wa.Client, evt.Info.Chat, evt.Info.Sender, protocolMsg.GetKey()) + ctx := evt.wa.UserLogin.Log. + With().Str("action", "get edit target message").Str("message_id", evt.Info.ID).Logger(). + WithContext(evt.wa.Main.Bridge.BackgroundCtx) + return msgconv.KeyToMessageID(ctx, evt.wa.Client, evt.Info.Chat, evt.Info.Sender, protocolMsg.GetKey()) } return "" } diff --git a/pkg/connector/handlematrix.go b/pkg/connector/handlematrix.go index 38ea4af..3abc38e 100644 --- a/pkg/connector/handlematrix.go +++ b/pkg/connector/handlematrix.go @@ -112,7 +112,7 @@ func (wa *WhatsAppClient) handleConvertedMatrixMessage(ctx context.Context, msg return nil, err } var pickedMessageID networkid.MessageID - if resp.Sender == wa.GetStore().GetLID() { + if resp.Sender == wa.GetStore().GetLID() && chatJID.Server != types.DefaultUserServer { pickedMessageID = wrappedMsgID2 msg.RemovePending(networkid.TransactionID(wrappedMsgID)) } else { diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index 9bac6ad..d362de2 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -251,12 +251,16 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) (success bool) { return } -func (wa *WhatsAppClient) rerouteWAMessage(ctx context.Context, info *types.MessageInfo) { +func (wa *WhatsAppClient) rerouteWAMessage(ctx context.Context, evtType string, info *types.MessageSource, msgID any) { + if info.Chat.Server == types.HiddenUserServer && info.SenderAlt.IsEmpty() { + info.SenderAlt, _ = wa.GetStore().LIDs.GetPNForLID(ctx, info.Sender) + } if info.Chat.Server == types.HiddenUserServer && info.Sender.ToNonAD() == info.Chat && info.SenderAlt.Server == types.DefaultUserServer { wa.UserLogin.Log.Debug(). Stringer("lid", info.Sender). Stringer("pn", info.SenderAlt). - Str("message_id", info.ID). + Any("message_id", msgID). + Str("evt_type", evtType). Msg("Forced LID DM sender to phone number in incoming message") info.Sender, info.SenderAlt = info.SenderAlt, info.Sender info.Chat = info.Sender.ToNonAD() @@ -264,21 +268,31 @@ func (wa *WhatsAppClient) rerouteWAMessage(ctx context.Context, info *types.Mess wa.UserLogin.Log.Debug(). Stringer("lid", info.Chat). Stringer("pn", info.RecipientAlt). - Str("message_id", info.ID). + Any("message_id", msgID). + Str("evt_type", evtType). Msg("Forced LID DM sender to phone number in own message sent from another device") info.Chat = info.RecipientAlt.ToNonAD() + if info.Sender.Server == types.HiddenUserServer { + info.Sender, info.SenderAlt = info.SenderAlt, info.Sender + if info.Sender.IsEmpty() { + info.Sender = wa.Device.GetJID() + info.Sender.Device = info.SenderAlt.Device + } + } } else if info.Sender.Server == types.BotServer && info.Chat.Server == types.HiddenUserServer { chatPN, err := wa.GetStore().LIDs.GetPNForLID(ctx, info.Chat) if err != nil { wa.UserLogin.Log.Err(err). - Str("message_id", info.ID). + Any("message_id", msgID). Stringer("lid", info.Chat). + Str("evt_type", evtType). Msg("Failed to get phone number of DM for incoming bot message") } else if !chatPN.IsEmpty() { wa.UserLogin.Log.Debug(). Stringer("lid", info.Chat). Stringer("pn", chatPN). - Str("message_id", info.ID). + Any("message_id", msgID). + Str("evt_type", evtType). Msg("Forced LID chat to phone number in bot message") info.Chat = chatPN } @@ -287,7 +301,7 @@ func (wa *WhatsAppClient) rerouteWAMessage(ctx context.Context, info *types.Mess func (wa *WhatsAppClient) handleWAMessage(ctx context.Context, evt *events.Message) (success bool) { success = true - wa.rerouteWAMessage(ctx, &evt.Info) + wa.rerouteWAMessage(ctx, "message", &evt.Info.MessageSource, evt.Info.ID) wa.UserLogin.Log.Trace(). Any("info", evt.Info). Any("payload", evt.Message). @@ -375,7 +389,7 @@ func (wa *WhatsAppClient) handleWAMessage(ctx context.Context, evt *events.Messa } func (wa *WhatsAppClient) handleWAUndecryptableMessage(ctx context.Context, evt *events.UndecryptableMessage) bool { - wa.rerouteWAMessage(ctx, &evt.Info) + wa.rerouteWAMessage(ctx, "undecryptable message", &evt.Info.MessageSource, evt.Info.ID) wa.UserLogin.Log.Debug(). Any("info", evt.Info). Bool("unavailable", evt.IsUnavailable). @@ -399,25 +413,7 @@ func (wa *WhatsAppClient) handleWAUndecryptableMessage(ctx context.Context, evt } func (wa *WhatsAppClient) handleWAReceipt(ctx context.Context, evt *events.Receipt) (success bool) { - if evt.Chat.Server == types.HiddenUserServer && evt.SenderAlt.IsEmpty() { - evt.SenderAlt, _ = wa.GetStore().LIDs.GetPNForLID(ctx, evt.Sender) - } - if evt.Chat.Server == types.HiddenUserServer && evt.Sender.ToNonAD() == evt.Chat && evt.SenderAlt.Server == types.DefaultUserServer { - wa.UserLogin.Log.Debug(). - Stringer("lid", evt.Sender). - Stringer("pn", evt.SenderAlt). - Strs("message_id", evt.MessageIDs). - Msg("Forced LID DM sender to phone number in incoming receipt") - evt.Sender, evt.SenderAlt = evt.SenderAlt, evt.Sender - evt.Chat = evt.Sender.ToNonAD() - } else if evt.Chat.Server == types.HiddenUserServer && evt.IsFromMe && evt.RecipientAlt.Server == types.DefaultUserServer { - wa.UserLogin.Log.Debug(). - Stringer("lid", evt.Chat). - Stringer("pn", evt.RecipientAlt). - Strs("message_id", evt.MessageIDs). - Msg("Forced LID DM sender to phone number in own receipt sent from another device") - evt.Chat = evt.RecipientAlt.ToNonAD() - } + wa.rerouteWAMessage(ctx, "receipt", &evt.MessageSource, evt.MessageIDs) if evt.IsFromMe && evt.Sender.Device == 0 { wa.phoneSeen(evt.Timestamp) } diff --git a/pkg/msgconv/from-whatsapp.go b/pkg/msgconv/from-whatsapp.go index a1156bc..c1f36ce 100644 --- a/pkg/msgconv/from-whatsapp.go +++ b/pkg/msgconv/from-whatsapp.go @@ -258,6 +258,7 @@ func (mc *MessageConverter) ToMatrix( chat, _ = waid.ParsePortalID(portal.ID) } // We reroute all DMs to the phone number JID, so reroute reply participants too + pcp = rerouteMessageKey(ctx, chat, pcp, getPortal(ctx).Metadata.(*waid.PortalMetadata).AddressingMode == types.AddressingModeLID) if store := getClient(ctx).Store; store != nil && chat.Server == types.DefaultUserServer && pcp.Server == types.HiddenUserServer { pcpPN, _ := store.LIDs.GetPNForLID(ctx, pcp) zerolog.Ctx(ctx).Debug(). diff --git a/pkg/msgconv/wa-poll.go b/pkg/msgconv/wa-poll.go index 97a75e2..6dac2d6 100644 --- a/pkg/msgconv/wa-poll.go +++ b/pkg/msgconv/wa-poll.go @@ -24,7 +24,6 @@ import ( "strings" "github.com/rs/zerolog" - "go.mau.fi/util/ptr" "go.mau.fi/whatsmeow" "go.mau.fi/whatsmeow/proto/waCommon" "go.mau.fi/whatsmeow/proto/waE2E" @@ -95,7 +94,31 @@ func (mc *MessageConverter) convertPollCreationMessage(ctx context.Context, msg }, msg.GetContextInfo() } -func KeyToMessageID(client *whatsmeow.Client, chat, sender types.JID, key *waCommon.MessageKey) networkid.MessageID { +func rerouteMessageKey(ctx context.Context, chat, sender types.JID, groupLIDAddressing bool) types.JID { + if store := getClient(ctx).Store; store != nil && chat.Server == types.DefaultUserServer && sender.Server == types.HiddenUserServer { + senderPN, _ := store.LIDs.GetPNForLID(ctx, sender) + zerolog.Ctx(ctx).Debug(). + Stringer("orig_participant", sender). + Stringer("rerouted_participant", senderPN). + Msg("Rerouting message key (PN recipient in LID DM)") + if !senderPN.IsEmpty() { + return senderPN + } + } else if store != nil && chat.Server == types.GroupServer && sender.Server == types.DefaultUserServer && groupLIDAddressing { + senderLID, _ := store.LIDs.GetLIDForPN(ctx, sender) + zerolog.Ctx(ctx).Debug(). + Stringer("orig_participant", sender). + Stringer("rerouted_participant", senderLID). + Msg("Rerouting message key (PN recipient in LID group)") + if !senderLID.IsEmpty() { + return senderLID + } + } + return sender +} + +func KeyToMessageID(ctx context.Context, client *whatsmeow.Client, chat, sender types.JID, key *waCommon.MessageKey) networkid.MessageID { + groupLIDAddressing := sender.Server == types.HiddenUserServer sender = sender.ToNonAD() var err error if !key.GetFromMe() { @@ -109,14 +132,21 @@ func KeyToMessageID(client *whatsmeow.Client, chat, sender types.JID, key *waCom sender.Server = types.DefaultUserServer } } else if chat.Server == types.DefaultUserServer || chat.Server == types.BotServer { - ownID := ptr.Val(client.Store.ID).ToNonAD() - if sender.User == ownID.User { + if sender.User == client.Store.GetJID().User || sender.User == client.Store.GetLID().User { + // Message key is not from the sender, but message sender (containing key) is me, + // so message key sender is the other user in the DM sender = chat } else { - sender = ownID + // Message key is not from the sender, but message sender (containing key) is not me, + // so message key sender is me + sender = client.Store.GetJID().ToNonAD() } } else { - // TODO log somehow? + zerolog.Ctx(ctx).Warn(). + Stringer("chat", chat). + Stringer("sender", sender). + Any("key", key). + Msg("Failed to get message ID from key") return "" } } @@ -127,6 +157,10 @@ func KeyToMessageID(client *whatsmeow.Client, chat, sender types.JID, key *waCom chat = remoteJID } } + sender = rerouteMessageKey( + context.WithValue(ctx, contextKeyClient, client), + chat, sender, groupLIDAddressing, + ) return waid.MakeMessageID(chat, sender, key.GetID()) } @@ -138,7 +172,7 @@ var failedPollUpdatePart = &bridgev2.ConvertedMessagePart{ func (mc *MessageConverter) convertPollUpdateMessage(ctx context.Context, info *types.MessageInfo, msg *waE2E.PollUpdateMessage) (*bridgev2.ConvertedMessagePart, *waE2E.ContextInfo) { log := zerolog.Ctx(ctx) - pollMessageID := KeyToMessageID(getClient(ctx), info.Chat, info.Sender, msg.PollCreationMessageKey) + pollMessageID := KeyToMessageID(ctx, getClient(ctx), info.Chat, info.Sender, msg.PollCreationMessageKey) pollMessage, err := mc.Bridge.DB.Message.GetPartByID(ctx, getPortal(ctx).Receiver, pollMessageID, "") if err != nil { log.Err(err).Msg("Failed to get poll update target message") From a291d40d66555883b9165458157f2f00f61b587e Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 11 Nov 2025 19:13:33 +0200 Subject: [PATCH 051/153] backfill: reroute LID DMs in history syncs --- pkg/connector/backfill.go | 14 ++++++++++++++ pkg/connector/handlewhatsapp.go | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/pkg/connector/backfill.go b/pkg/connector/backfill.go index 2c3a14e..dcc4bca 100644 --- a/pkg/connector/backfill.go +++ b/pkg/connector/backfill.go @@ -193,6 +193,20 @@ func (wa *WhatsAppClient) handleWAHistorySync(ctx context.Context, evt *waHistor log.Debug().Stringer("chat_jid", jid).Msg("Skipping broadcast list in history sync") continue } + if jid.Server == types.HiddenUserServer { + pn, err := wa.GetStore().LIDs.GetPNForLID(ctx, jid) + if err != nil { + log.Err(err).Stringer("lid", jid).Msg("Failed to get PN for LID in history sync") + } else if pn.IsEmpty() { + log.Warn().Stringer("lid", jid).Msg("No PN found for LID in history sync") + } else { + log.Debug(). + Stringer("lid", jid). + Stringer("pn", pn). + Msg("Rerouting LID DM to phone number in history sync") + jid = pn + } + } totalMessageCount += len(conv.GetMessages()) log := log.With(). Stringer("chat_jid", jid). diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index d362de2..a542694 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -275,7 +275,7 @@ func (wa *WhatsAppClient) rerouteWAMessage(ctx context.Context, evtType string, if info.Sender.Server == types.HiddenUserServer { info.Sender, info.SenderAlt = info.SenderAlt, info.Sender if info.Sender.IsEmpty() { - info.Sender = wa.Device.GetJID() + info.Sender = wa.GetStore().GetJID() info.Sender.Device = info.SenderAlt.Device } } From 88a7a2b8d7d04e1b3a380aa7f3c2b8ad580fbbaf Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 13 Nov 2025 15:03:59 +0200 Subject: [PATCH 052/153] chatinfo: don't apply history sync metadata on later resyncs --- pkg/connector/backfill.go | 2 +- pkg/connector/chatinfo.go | 26 ++++++++++++++------------ pkg/connector/commands.go | 11 +++++++---- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/pkg/connector/backfill.go b/pkg/connector/backfill.go index dcc4bca..5d2e9ae 100644 --- a/pkg/connector/backfill.go +++ b/pkg/connector/backfill.go @@ -356,7 +356,7 @@ func (wa *WhatsAppClient) createPortalsFromHistorySync(ctx context.Context) { log.Warn().Err(ctx.Err()).Msg("Context cancelled, stopping history sync portal creation") return } - wrappedInfo, err := wa.getChatInfo(ctx, conv.ChatJID, conv) + wrappedInfo, err := wa.getChatInfo(ctx, conv.ChatJID, conv, true) if errors.Is(err, whatsmeow.ErrNotInGroup) { log.Debug().Stringer("chat_jid", conv.ChatJID). Msg("Skipping creating room because the user is not a participant") diff --git a/pkg/connector/chatinfo.go b/pkg/connector/chatinfo.go index c3545f5..5555566 100644 --- a/pkg/connector/chatinfo.go +++ b/pkg/connector/chatinfo.go @@ -26,10 +26,10 @@ func (wa *WhatsAppClient) GetChatInfo(ctx context.Context, portal *bridgev2.Port if err != nil { return nil, err } - return wa.getChatInfo(ctx, portalJID, nil) + return wa.getChatInfo(ctx, portalJID, nil, portal.MXID == "") } -func (wa *WhatsAppClient) getChatInfo(ctx context.Context, portalJID types.JID, conv *wadb.Conversation) (wrapped *bridgev2.ChatInfo, err error) { +func (wa *WhatsAppClient) getChatInfo(ctx context.Context, portalJID types.JID, conv *wadb.Conversation, isNew bool) (wrapped *bridgev2.ChatInfo, err error) { switch portalJID.Server { case types.DefaultUserServer, types.HiddenUserServer, types.BotServer: wrapped = wa.wrapDMInfo(ctx, portalJID) @@ -55,20 +55,22 @@ func (wa *WhatsAppClient) getChatInfo(ctx context.Context, portalJID types.JID, default: return nil, fmt.Errorf("unsupported server %s", portalJID.Server) } - wa.addExtrasToWrapped(ctx, portalJID, wrapped, conv) + wa.addExtrasToWrapped(ctx, portalJID, wrapped, conv, isNew) return wrapped, nil } -func (wa *WhatsAppClient) addExtrasToWrapped(ctx context.Context, portalJID types.JID, wrapped *bridgev2.ChatInfo, conv *wadb.Conversation) { - if conv == nil { - var err error - conv, err = wa.Main.DB.Conversation.Get(ctx, wa.UserLogin.ID, portalJID) - if err != nil { - zerolog.Ctx(ctx).Warn().Err(err).Msg("Failed to get history sync conversation info") +func (wa *WhatsAppClient) addExtrasToWrapped(ctx context.Context, portalJID types.JID, wrapped *bridgev2.ChatInfo, conv *wadb.Conversation, isNew bool) { + if isNew { + if conv == nil { + var err error + conv, err = wa.Main.DB.Conversation.Get(ctx, wa.UserLogin.ID, portalJID) + if err != nil { + zerolog.Ctx(ctx).Warn().Err(err).Msg("Failed to get history sync conversation info") + } + } + if conv != nil { + wa.applyHistoryInfo(wrapped, conv) } - } - if conv != nil { - wa.applyHistoryInfo(wrapped, conv) } wa.applyChatSettings(ctx, portalJID, wrapped) } diff --git a/pkg/connector/commands.go b/pkg/connector/commands.go index e86a6f9..a0ebfcf 100644 --- a/pkg/connector/commands.go +++ b/pkg/connector/commands.go @@ -17,6 +17,7 @@ package connector import ( + "context" "errors" "fmt" "html" @@ -120,9 +121,6 @@ func fnSync(ce *commands.Event) { return } for _, group := range groups { - wrapped := wa.wrapGroupInfo(ce.Ctx, group) - wrapped.ExtraUpdates = bridgev2.MergeExtraUpdaters(wrapped.ExtraUpdates, updatePortalLastSyncAt) - wa.addExtrasToWrapped(ce.Ctx, group.JID, wrapped, nil) login.QueueRemoteEvent(&simplevent.ChatResync{ EventMeta: simplevent.EventMeta{ Type: bridgev2.RemoteEventChatResync, @@ -130,7 +128,12 @@ func fnSync(ce *commands.Event) { LogContext: logContext, CreatePortal: true, }, - ChatInfo: wrapped, + GetChatInfoFunc: func(ctx context.Context, portal *bridgev2.Portal) (*bridgev2.ChatInfo, error) { + wrapped := wa.wrapGroupInfo(ce.Ctx, group) + wrapped.ExtraUpdates = bridgev2.MergeExtraUpdaters(wrapped.ExtraUpdates, updatePortalLastSyncAt) + wa.addExtrasToWrapped(ce.Ctx, group.JID, wrapped, nil, portal.MXID == "") + return wrapped, nil + }, }) } ce.Reply("Queued syncs for %d groups", len(groups)) From 10a8c64d63eb0c8b497c3536949d2fe722b71442 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 13 Nov 2025 18:09:26 +0200 Subject: [PATCH 053/153] backfill: fix panic when reaction is missing sender --- pkg/connector/backfill.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/connector/backfill.go b/pkg/connector/backfill.go index 5d2e9ae..c9aa521 100644 --- a/pkg/connector/backfill.go +++ b/pkg/connector/backfill.go @@ -548,10 +548,10 @@ func (wa *WhatsAppClient) convertHistorySyncMessage( TxnID: networkid.TransactionID(waid.MakeMessageID(info.Chat, info.Sender, info.ID)), Timestamp: info.Timestamp, StreamOrder: info.Timestamp.Unix(), - Reactions: make([]*bridgev2.BackfillReaction, len(reactions)), + Reactions: make([]*bridgev2.BackfillReaction, 0, len(reactions)), } mediaReq := wa.processFailedMedia(ctx, portal.PortalKey, wrapped.ID, wrapped.ConvertedMessage, true) - for i, reaction := range reactions { + for _, reaction := range reactions { var sender types.JID if reaction.GetKey().GetFromMe() { sender = wa.JID @@ -563,12 +563,12 @@ func (wa *WhatsAppClient) convertHistorySyncMessage( if sender.IsEmpty() { continue } - wrapped.Reactions[i] = &bridgev2.BackfillReaction{ + wrapped.Reactions = append(wrapped.Reactions, &bridgev2.BackfillReaction{ TargetPart: ptr.Ptr(networkid.PartID("")), Timestamp: time.UnixMilli(reaction.GetSenderTimestampMS()), Sender: wa.makeEventSender(ctx, sender), Emoji: reaction.GetText(), - } + }) } return wrapped, mediaReq } From 0f7582cf198f5d04e2c8cd4bb11630a3b64ffc74 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 14 Nov 2025 17:47:58 +0200 Subject: [PATCH 054/153] connector: add migration to delete LID DM portals --- pkg/connector/connector.go | 75 +++++++++++++++++++ .../wadb/upgrades/00-latest-schema.sql | 2 +- .../upgrades/08-may-need-lid-dm-deletion.sql | 2 + 3 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 pkg/connector/wadb/upgrades/08-may-need-lid-dm-deletion.sql diff --git a/pkg/connector/connector.go b/pkg/connector/connector.go index 25a0707..af23ed4 100644 --- a/pkg/connector/connector.go +++ b/pkg/connector/connector.go @@ -25,6 +25,7 @@ import ( "sync/atomic" "github.com/lib/pq" + "github.com/rs/zerolog" "go.mau.fi/util/dbutil" "go.mau.fi/util/random" "go.mau.fi/whatsmeow" @@ -32,16 +33,19 @@ import ( "go.mau.fi/whatsmeow/store" "go.mau.fi/whatsmeow/store/sqlstore" whatsmeowUpgrades "go.mau.fi/whatsmeow/store/sqlstore/upgrades" + "go.mau.fi/whatsmeow/types" waLog "go.mau.fi/whatsmeow/util/log" "google.golang.org/protobuf/proto" "maunium.net/go/mautrix/bridgev2" "maunium.net/go/mautrix/bridgev2/commands" + "maunium.net/go/mautrix/bridgev2/database" "maunium.net/go/mautrix/bridgev2/networkid" "maunium.net/go/mautrix/event" "maunium.net/go/mautrix/id" "go.mau.fi/mautrix-whatsapp/pkg/connector/wadb" "go.mau.fi/mautrix-whatsapp/pkg/msgconv" + "go.mau.fi/mautrix-whatsapp/pkg/waid" ) type WhatsAppConnector struct { @@ -149,9 +153,80 @@ func (wa *WhatsAppConnector) Start(ctx context.Context) error { return bridgev2.DBUpgradeError{Err: err, Section: "whatsapp"} } + if !wa.Bridge.Background && wa.Bridge.DB.KV.Get(ctx, "whatsapp_lid_dms_deleted") == "false" { + wa.deleteLIDDMsMigration(ctx) + } + return nil } +func (wa *WhatsAppConnector) deleteLIDDMsMigration(ctx context.Context) { + log := zerolog.Ctx(ctx).With().Str("action", "delete lid dms").Logger() + portals, err := wa.Bridge.GetAllPortalsWithMXID(ctx) + if err != nil { + log.Err(err).Msg("Failed to get portals for LID DM deletion") + return + } + defer wa.Bridge.DB.KV.Set(ctx, "whatsapp_lid_dms_deleted", "true") + if len(portals) == 0 { + log.Debug().Msg("No portals found") + return + } + portalsByKey := make(map[networkid.PortalKey]*bridgev2.Portal, len(portals)) + for _, p := range portals { + if p.Receiver == "" || p.RoomType != database.RoomTypeDM { + continue + } + portalsByKey[p.PortalKey] = p + } + _, err = wa.DB.Exec(ctx, "DELETE FROM whatsapp_history_sync_conversation WHERE chat_jid LIKE '%@lid'") + if err != nil { + log.Err(err).Msg("Failed to remove LID conversations from history sync") + } + for key, portal := range portalsByKey { + parsedID, err := waid.ParsePortalID(key.ID) + if err != nil { + log.Warn().Err(err).Str("portal_id", string(key.ID)).Msg("Failed to parse portal ID") + continue + } else if parsedID.Server != types.HiddenUserServer { + continue + } + var pnStr string + err = wa.DB.QueryRow(ctx, "SELECT pn FROM whatsmeow_lid_map WHERE lid=$1", parsedID.User).Scan(&pnStr) + if err != nil { + log.Warn().Err(err).Str("portal_id", string(key.ID)).Msg("Failed to get PN for LID portal") + continue + } + key.ID = waid.MakePortalID(types.JID{User: pnStr, Server: types.DefaultUserServer}) + _, pnPortalExists := portalsByKey[key] + if !pnPortalExists { + log.Warn().Str("portal_id", string(key.ID)).Msg("PN portal does not exist, not deleting LID DM") + continue + } + err = portal.Delete(ctx) + if err != nil { + log.Err(err). + Object("portal_key", portal.PortalKey). + Stringer("portal_mxid", portal.MXID). + Msg("Failed to delete LID DM portal from database") + continue + } + err = wa.Bridge.Bot.DeleteRoom(ctx, portal.MXID, false) + if err != nil { + log.Err(err). + Object("portal_key", portal.PortalKey). + Stringer("portal_mxid", portal.MXID). + Msg("Failed to delete LID DM portal from Matrix") + continue + } + log.Debug(). + Object("portal_key", portal.PortalKey). + Stringer("portal_mxid", portal.MXID). + Msg("Deleted LID DM portal") + } + log.Info().Msg("Finished deleting LID DM portals") +} + func (wa *WhatsAppConnector) Stop() { if stop := wa.stopMediaEditCacheLoop.Swap(nil); stop != nil { (*stop)() diff --git a/pkg/connector/wadb/upgrades/00-latest-schema.sql b/pkg/connector/wadb/upgrades/00-latest-schema.sql index 97275f5..d9aeac9 100644 --- a/pkg/connector/wadb/upgrades/00-latest-schema.sql +++ b/pkg/connector/wadb/upgrades/00-latest-schema.sql @@ -1,4 +1,4 @@ --- v0 -> v7 (compatible with v3+): Latest revision +-- v0 -> v8 (compatible with v3+): Latest revision CREATE TABLE whatsapp_poll_option_id ( bridge_id TEXT NOT NULL, diff --git a/pkg/connector/wadb/upgrades/08-may-need-lid-dm-deletion.sql b/pkg/connector/wadb/upgrades/08-may-need-lid-dm-deletion.sql new file mode 100644 index 0000000..d5b4ca0 --- /dev/null +++ b/pkg/connector/wadb/upgrades/08-may-need-lid-dm-deletion.sql @@ -0,0 +1,2 @@ +-- v8 (compatible with v3+): Mark LID DMs for deletion +INSERT INTO kv_store (bridge_id, key, value) VALUES ('', 'whatsapp_lid_dms_deleted', 'false'); From 1caa0cb78d77ac15c90decc9bddc1fe86720ec18 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sat, 15 Nov 2025 22:00:36 +0200 Subject: [PATCH 055/153] dependencies: update whatsmeow --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index de830cc..d65e28c 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.2 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20251110110826-a121e2b9cd1e + go.mau.fi/whatsmeow v0.0.0-20251115195115-7159d9053646 golang.org/x/image v0.32.0 golang.org/x/net v0.46.0 golang.org/x/sync v0.17.0 diff --git a/go.sum b/go.sum index 992979e..27ba0c9 100644 --- a/go.sum +++ b/go.sum @@ -79,8 +79,8 @@ go.mau.fi/util v0.9.2 h1:+S4Z03iCsGqU2WY8X2gySFsFjaLlUHFRDVCYvVwynKM= go.mau.fi/util v0.9.2/go.mod h1:055elBBCJSdhRsmub7ci9hXZPgGr1U6dYg44cSgRgoU= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20251110110826-a121e2b9cd1e h1:I93PJ14VjWNTJ2BVm2jT9WQ60fMDNmGng6qZs2CzYpg= -go.mau.fi/whatsmeow v0.0.0-20251110110826-a121e2b9cd1e/go.mod h1:RwBrMQAWCHGzMdDZ6EwjcY4Aj3g8Efx8c7GACTdiAME= +go.mau.fi/whatsmeow v0.0.0-20251115195115-7159d9053646 h1:kPrIYSYxrvK2jJQPey7RXWLrP/c9psz58mZWD93BRao= +go.mau.fi/whatsmeow v0.0.0-20251115195115-7159d9053646/go.mod h1:RwBrMQAWCHGzMdDZ6EwjcY4Aj3g8Efx8c7GACTdiAME= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= From f77169b4262bad8765e935b6a4bb0d3cec447a8b Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 16 Nov 2025 13:43:08 +0200 Subject: [PATCH 056/153] Bump version to v25.11 --- CHANGELOG.md | 8 +++++++- cmd/mautrix-whatsapp/main.go | 2 +- go.mod | 22 ++++++++++---------- go.sum | 40 ++++++++++++++++++------------------ 4 files changed, 39 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b1a81a3..2c199b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,21 @@ -# v25.11 (unreleased) +# v25.11 * Added interface support for notifying about failed invites when creating a group and sending the invites via DM (only applicable to provisioning API). +* Added migration to automatically delete duplicate LID DM portals that were + created earlier. * Changed contact list API to only include actual phone contacts. * Removed extra unrecognized message notice when receiving live photos (bridging the live photo video is not currently planned). * Fixed pairing not working with latest WhatsApp Android version. * Fixed replies, read receipts and typing notifications not being bridged correctly after DM LID migration. +* Fixed backfill creating duplicate portals if history sync contains both LID + and phone number DM data. * Fixed some cases of LID and phone number user infos getting out of sync. * Fixed muting chat forever not being bridged correctly from WhatsApp. +* Fixed old mutes being re-applied on chat resync in some cases. +* Fixed backfilling failing if some reactions were missing sender info. * Fixed space not being deleted when leaving community on WhatsApp. * Fixed sticker size metadata on Matrix not matching how native WhatsApp Web renders them. diff --git a/cmd/mautrix-whatsapp/main.go b/cmd/mautrix-whatsapp/main.go index a3e0665..b71912f 100644 --- a/cmd/mautrix-whatsapp/main.go +++ b/cmd/mautrix-whatsapp/main.go @@ -18,7 +18,7 @@ var m = mxmain.BridgeMain{ Name: "mautrix-whatsapp", URL: "https://github.com/mautrix/whatsapp", Description: "A Matrix-WhatsApp puppeting bridge.", - Version: "25.10", + Version: "25.11", SemCalVer: true, Connector: &connector.WhatsAppConnector{}, } diff --git a/go.mod b/go.mod index d65e28c..d03bb19 100644 --- a/go.mod +++ b/go.mod @@ -2,20 +2,20 @@ module go.mau.fi/mautrix-whatsapp go 1.24.0 -toolchain go1.25.3 +toolchain go1.25.4 require ( github.com/lib/pq v1.10.9 github.com/rs/zerolog v1.34.0 - go.mau.fi/util v0.9.2 + go.mau.fi/util v0.9.3 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20251115195115-7159d9053646 - golang.org/x/image v0.32.0 - golang.org/x/net v0.46.0 - golang.org/x/sync v0.17.0 + go.mau.fi/whatsmeow v0.0.0-20251116104239-3aca43070cd4 + golang.org/x/image v0.33.0 + golang.org/x/net v0.47.0 + golang.org/x/sync v0.18.0 google.golang.org/protobuf v1.36.10 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.25.3-0.20251109094010-14e16a3a8190 + maunium.net/go/mautrix v0.26.0 ) require ( @@ -41,10 +41,10 @@ require ( github.com/yuin/goldmark v1.7.13 // indirect go.mau.fi/libsignal v0.2.1 // indirect go.mau.fi/zeroconfig v0.2.0 // indirect - golang.org/x/crypto v0.43.0 // indirect - golang.org/x/exp v0.0.0-20251009144603-d2f985daa21b // indirect - golang.org/x/sys v0.37.0 // indirect - golang.org/x/text v0.30.0 // indirect + golang.org/x/crypto v0.44.0 // indirect + golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6 // indirect + golang.org/x/sys v0.38.0 // indirect + golang.org/x/text v0.31.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect maunium.net/go/mauflag v1.0.0 // indirect diff --git a/go.sum b/go.sum index 27ba0c9..6a9f38b 100644 --- a/go.sum +++ b/go.sum @@ -75,31 +75,31 @@ github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA= github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= go.mau.fi/libsignal v0.2.1 h1:vRZG4EzTn70XY6Oh/pVKrQGuMHBkAWlGRC22/85m9L0= go.mau.fi/libsignal v0.2.1/go.mod h1:iVvjrHyfQqWajOUaMEsIfo3IqgVMrhWcPiiEzk7NgoU= -go.mau.fi/util v0.9.2 h1:+S4Z03iCsGqU2WY8X2gySFsFjaLlUHFRDVCYvVwynKM= -go.mau.fi/util v0.9.2/go.mod h1:055elBBCJSdhRsmub7ci9hXZPgGr1U6dYg44cSgRgoU= +go.mau.fi/util v0.9.3 h1:aqNF8KDIN8bFpFbybSk+mEBil7IHeBwlujfyTnvP0uU= +go.mau.fi/util v0.9.3/go.mod h1:krWWfBM1jWTb5f8NCa2TLqWMQuM81X7TGQjhMjBeXmQ= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20251115195115-7159d9053646 h1:kPrIYSYxrvK2jJQPey7RXWLrP/c9psz58mZWD93BRao= -go.mau.fi/whatsmeow v0.0.0-20251115195115-7159d9053646/go.mod h1:RwBrMQAWCHGzMdDZ6EwjcY4Aj3g8Efx8c7GACTdiAME= +go.mau.fi/whatsmeow v0.0.0-20251116104239-3aca43070cd4 h1:7hXdxCFs2Me4nypiWjdBNonaFrPfmYJvEtTOwLctSHU= +go.mau.fi/whatsmeow v0.0.0-20251116104239-3aca43070cd4/go.mod h1:5aYaEa3FF5e5XWsA8Xa80ttUXZvb6HyaBGgo2SfzUkE= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= -golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= -golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= -golang.org/x/exp v0.0.0-20251009144603-d2f985daa21b h1:18qgiDvlvH7kk8Ioa8Ov+K6xCi0GMvmGfGW0sgd/SYA= -golang.org/x/exp v0.0.0-20251009144603-d2f985daa21b/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70= -golang.org/x/image v0.32.0 h1:6lZQWq75h7L5IWNk0r+SCpUJ6tUVd3v4ZHnbRKLkUDQ= -golang.org/x/image v0.32.0/go.mod h1:/R37rrQmKXtO6tYXAjtDLwQgFLHmhW+V6ayXlxzP2Pc= -golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= -golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= -golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU= +golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc= +golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6 h1:zfMcR1Cs4KNuomFFgGefv5N0czO2XZpUbxGUy8i8ug0= +golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0= +golang.org/x/image v0.33.0 h1:LXRZRnv1+zGd5XBUVRFmYEphyyKJjQjCRiOuAP3sZfQ= +golang.org/x/image v0.33.0/go.mod h1:DD3OsTYT9chzuzTQt+zMcOlBHgfoKQb1gry8p76Y1sc= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= +golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= -golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= -golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -111,5 +111,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.25.3-0.20251109094010-14e16a3a8190 h1:5v25ZS99ilLXHjYX3uMiyHGBNhFPXgOgGfr9GkakRTg= -maunium.net/go/mautrix v0.25.3-0.20251109094010-14e16a3a8190/go.mod h1:EWgYyp2iFZP7pnSm+rufHlO8YVnA2KnoNBDpwekiAwI= +maunium.net/go/mautrix v0.26.0 h1:valc2VmZF+oIY4bMq4Cd5H9cEKMRe8eP4FM7iiaYLxI= +maunium.net/go/mautrix v0.26.0/go.mod h1:NWMv+243NX/gDrLofJ2nNXJPrG8vzoM+WUCWph85S6Q= From d3cfd0868fac02343a5608d10a3c6664ddc43435 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 19 Nov 2025 15:07:08 +0200 Subject: [PATCH 057/153] dependencies: update mautrix-go --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d03bb19..2b6024c 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( golang.org/x/sync v0.18.0 google.golang.org/protobuf v1.36.10 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.26.0 + maunium.net/go/mautrix v0.26.1-0.20251119111538-57657d54eeac ) require ( diff --git a/go.sum b/go.sum index 6a9f38b..6db9f1c 100644 --- a/go.sum +++ b/go.sum @@ -111,5 +111,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.26.0 h1:valc2VmZF+oIY4bMq4Cd5H9cEKMRe8eP4FM7iiaYLxI= -maunium.net/go/mautrix v0.26.0/go.mod h1:NWMv+243NX/gDrLofJ2nNXJPrG8vzoM+WUCWph85S6Q= +maunium.net/go/mautrix v0.26.1-0.20251119111538-57657d54eeac h1:zRQXrl+c9Isc1pJ7/MAdov8OKt+MVPGh5qjTzefxb+k= +maunium.net/go/mautrix v0.26.1-0.20251119111538-57657d54eeac/go.mod h1:NWMv+243NX/gDrLofJ2nNXJPrG8vzoM+WUCWph85S6Q= From 7502fa37d45439e0b95374dd4ccb6ee74ccbbda3 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 19 Nov 2025 23:57:31 +0200 Subject: [PATCH 058/153] dependencies: update mautrix-go again --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 2b6024c..885fa7d 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( golang.org/x/sync v0.18.0 google.golang.org/protobuf v1.36.10 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.26.1-0.20251119111538-57657d54eeac + maunium.net/go/mautrix v0.26.1-0.20251119212156-1fac8ceb6653 ) require ( diff --git a/go.sum b/go.sum index 6db9f1c..6f18633 100644 --- a/go.sum +++ b/go.sum @@ -111,5 +111,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.26.1-0.20251119111538-57657d54eeac h1:zRQXrl+c9Isc1pJ7/MAdov8OKt+MVPGh5qjTzefxb+k= -maunium.net/go/mautrix v0.26.1-0.20251119111538-57657d54eeac/go.mod h1:NWMv+243NX/gDrLofJ2nNXJPrG8vzoM+WUCWph85S6Q= +maunium.net/go/mautrix v0.26.1-0.20251119212156-1fac8ceb6653 h1:z62cPre4V+NGIonN5qlvt1ZtZH5/93ix9DR7KR8yImU= +maunium.net/go/mautrix v0.26.1-0.20251119212156-1fac8ceb6653/go.mod h1:NWMv+243NX/gDrLofJ2nNXJPrG8vzoM+WUCWph85S6Q= From 3a65bdaf1e3416a60f05e5abee0f9a50e639e5c1 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 26 Nov 2025 19:13:48 +0200 Subject: [PATCH 059/153] directmedia: add debug log for missing avatars --- pkg/connector/directmedia.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pkg/connector/directmedia.go b/pkg/connector/directmedia.go index 2b289b8..f91c28d 100644 --- a/pkg/connector/directmedia.go +++ b/pkg/connector/directmedia.go @@ -29,6 +29,7 @@ import ( "github.com/rs/zerolog" "go.mau.fi/util/exsync" + "go.mau.fi/util/ptr" "go.mau.fi/whatsmeow" "go.mau.fi/whatsmeow/proto/waMmsRetry" "go.mau.fi/whatsmeow/types/events" @@ -85,7 +86,7 @@ func (wa *WhatsAppConnector) downloadAvatarDirectMedia(ctx context.Context, pars return nil, fmt.Errorf("failed to get avatar cache entry: %w", err) } if cachedInfo != nil && cachedInfo.Gone { - return nil, mautrix.MNotFound.WithMessage("Avatar is no longer available") + return nil, mautrix.MNotFound.WithMessage("Avatar is no longer available (cached response)") } else if cachedInfo == nil || cachedInfo.Expiry.Time.Before(time.Now().Add(5*time.Minute)) { zerolog.Ctx(ctx).Debug(). Str("avatar_id", parsedID.Avatar.AvatarID). @@ -96,6 +97,13 @@ func (wa *WhatsAppConnector) downloadAvatarDirectMedia(ctx context.Context, pars if errors.Is(err, whatsmeow.ErrProfilePictureNotSet) || errors.Is(err, whatsmeow.ErrProfilePictureUnauthorized) || (err == nil && (avatar == nil || avatar.ID != parsedID.Avatar.AvatarID)) { + zerolog.Ctx(ctx).Debug(). + Err(err). + Stringer("target_jid", parsedID.Avatar.TargetJID). + Bool("is_community", parsedID.Avatar.Community). + Str("wanted_avatar_id", parsedID.Avatar.AvatarID). + Str("got_avatar_id", ptr.Val(avatar).ID). + Msg("Avatar is no longer available") err = wa.DB.AvatarCache.Put(ctx, &wadb.AvatarCacheEntry{ EntityJID: parsedID.Avatar.TargetJID, AvatarID: parsedID.Avatar.AvatarID, From 8b2106b9bc9135f11e6f0fedfe800084d3f82045 Mon Sep 17 00:00:00 2001 From: Nick Mills-Barrett Date: Thu, 27 Nov 2025 14:08:50 +0000 Subject: [PATCH 060/153] dependencies: update whatsmeow --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 885fa7d..a7f52f1 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.3 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20251116104239-3aca43070cd4 + go.mau.fi/whatsmeow v0.0.0-20251127132918-b9ac3d51d746 golang.org/x/image v0.33.0 golang.org/x/net v0.47.0 golang.org/x/sync v0.18.0 diff --git a/go.sum b/go.sum index 6f18633..885f90d 100644 --- a/go.sum +++ b/go.sum @@ -79,8 +79,8 @@ go.mau.fi/util v0.9.3 h1:aqNF8KDIN8bFpFbybSk+mEBil7IHeBwlujfyTnvP0uU= go.mau.fi/util v0.9.3/go.mod h1:krWWfBM1jWTb5f8NCa2TLqWMQuM81X7TGQjhMjBeXmQ= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20251116104239-3aca43070cd4 h1:7hXdxCFs2Me4nypiWjdBNonaFrPfmYJvEtTOwLctSHU= -go.mau.fi/whatsmeow v0.0.0-20251116104239-3aca43070cd4/go.mod h1:5aYaEa3FF5e5XWsA8Xa80ttUXZvb6HyaBGgo2SfzUkE= +go.mau.fi/whatsmeow v0.0.0-20251127132918-b9ac3d51d746 h1:51hAK0a+KA4BD7MgqfVd6dbJd5cJBhotOlhWUybYCn0= +go.mau.fi/whatsmeow v0.0.0-20251127132918-b9ac3d51d746/go.mod h1:5aYaEa3FF5e5XWsA8Xa80ttUXZvb6HyaBGgo2SfzUkE= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU= From 89dee0afd9ce1c07f5ad77f663ae8a280b232619 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 28 Nov 2025 22:03:27 +0200 Subject: [PATCH 061/153] build.sh: replace with go tool Closes #587 --- build.sh | 4 +--- go.mod | 13 ++++++++----- go.sum | 22 ++++++++++++---------- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/build.sh b/build.sh index 0676fdd..2442135 100755 --- a/build.sh +++ b/build.sh @@ -1,4 +1,2 @@ #!/bin/sh -MAUTRIX_VERSION=$(cat go.mod | grep 'maunium.net/go/mautrix ' | awk '{ print $2 }') -GO_LDFLAGS="-s -w -X main.Tag=$(git describe --exact-match --tags 2>/dev/null) -X main.Commit=$(git rev-parse HEAD) -X 'main.BuildTime=`date -Iseconds`' -X 'maunium.net/go/mautrix.GoModVersion=$MAUTRIX_VERSION'" -go build -ldflags="$GO_LDFLAGS" "$@" ./cmd/mautrix-whatsapp +BINARY_NAME=mautrix-whatsapp go tool maubuild "$@" diff --git a/go.mod b/go.mod index a7f52f1..875c93c 100644 --- a/go.mod +++ b/go.mod @@ -4,10 +4,12 @@ go 1.24.0 toolchain go1.25.4 +tool go.mau.fi/util/cmd/maubuild + require ( github.com/lib/pq v1.10.9 github.com/rs/zerolog v1.34.0 - go.mau.fi/util v0.9.3 + go.mau.fi/util v0.9.4-0.20251128195053-c7fab4d88a04 go.mau.fi/webp v0.2.0 go.mau.fi/whatsmeow v0.0.0-20251127132918-b9ac3d51d746 golang.org/x/image v0.33.0 @@ -15,7 +17,7 @@ require ( golang.org/x/sync v0.18.0 google.golang.org/protobuf v1.36.10 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.26.1-0.20251119212156-1fac8ceb6653 + maunium.net/go/mautrix v0.26.1-0.20251128114054-1d1ecb228668 ) require ( @@ -29,7 +31,7 @@ require ( github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-sqlite3 v1.14.32 // indirect - github.com/petermattis/goid v0.0.0-20250904145737-900bdf8bb490 // indirect + github.com/petermattis/goid v0.0.0-20251121121749-a11dd1a45f9a // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/rs/xid v1.6.0 // indirect github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect @@ -41,8 +43,9 @@ require ( github.com/yuin/goldmark v1.7.13 // indirect go.mau.fi/libsignal v0.2.1 // indirect go.mau.fi/zeroconfig v0.2.0 // indirect - golang.org/x/crypto v0.44.0 // indirect - golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6 // indirect + golang.org/x/crypto v0.45.0 // indirect + golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 // indirect + golang.org/x/mod v0.30.0 // indirect golang.org/x/sys v0.38.0 // indirect golang.org/x/text v0.31.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect diff --git a/go.sum b/go.sum index 885f90d..6141c5a 100644 --- a/go.sum +++ b/go.sum @@ -40,8 +40,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs= github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/petermattis/goid v0.0.0-20250904145737-900bdf8bb490 h1:QTvNkZ5ylY0PGgA+Lih+GdboMLY/G9SEGLMEGVjTVA4= -github.com/petermattis/goid v0.0.0-20250904145737-900bdf8bb490/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +github.com/petermattis/goid v0.0.0-20251121121749-a11dd1a45f9a h1:VweslR2akb/ARhXfqSfRbj1vpWwYXf3eeAUyw/ndms0= +github.com/petermattis/goid v0.0.0-20251121121749-a11dd1a45f9a/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -75,20 +75,22 @@ github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA= github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= go.mau.fi/libsignal v0.2.1 h1:vRZG4EzTn70XY6Oh/pVKrQGuMHBkAWlGRC22/85m9L0= go.mau.fi/libsignal v0.2.1/go.mod h1:iVvjrHyfQqWajOUaMEsIfo3IqgVMrhWcPiiEzk7NgoU= -go.mau.fi/util v0.9.3 h1:aqNF8KDIN8bFpFbybSk+mEBil7IHeBwlujfyTnvP0uU= -go.mau.fi/util v0.9.3/go.mod h1:krWWfBM1jWTb5f8NCa2TLqWMQuM81X7TGQjhMjBeXmQ= +go.mau.fi/util v0.9.4-0.20251128195053-c7fab4d88a04 h1:1xsqu1gqwLUBEolvrsURhj/A3e9GACg95i8gITI0Be0= +go.mau.fi/util v0.9.4-0.20251128195053-c7fab4d88a04/go.mod h1:viDmhBOAFfcqDdKSk53EPJV3N4Mi8Jst5/ahGJ/vwsA= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= go.mau.fi/whatsmeow v0.0.0-20251127132918-b9ac3d51d746 h1:51hAK0a+KA4BD7MgqfVd6dbJd5cJBhotOlhWUybYCn0= go.mau.fi/whatsmeow v0.0.0-20251127132918-b9ac3d51d746/go.mod h1:5aYaEa3FF5e5XWsA8Xa80ttUXZvb6HyaBGgo2SfzUkE= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= -golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU= -golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc= -golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6 h1:zfMcR1Cs4KNuomFFgGefv5N0czO2XZpUbxGUy8i8ug0= -golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0= +golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= +golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= +golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 h1:DHNhtq3sNNzrvduZZIiFyXWOL9IWaDPHqTnLJp+rCBY= +golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0= golang.org/x/image v0.33.0 h1:LXRZRnv1+zGd5XBUVRFmYEphyyKJjQjCRiOuAP3sZfQ= golang.org/x/image v0.33.0/go.mod h1:DD3OsTYT9chzuzTQt+zMcOlBHgfoKQb1gry8p76Y1sc= +golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk= +golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc= golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= @@ -111,5 +113,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.26.1-0.20251119212156-1fac8ceb6653 h1:z62cPre4V+NGIonN5qlvt1ZtZH5/93ix9DR7KR8yImU= -maunium.net/go/mautrix v0.26.1-0.20251119212156-1fac8ceb6653/go.mod h1:NWMv+243NX/gDrLofJ2nNXJPrG8vzoM+WUCWph85S6Q= +maunium.net/go/mautrix v0.26.1-0.20251128114054-1d1ecb228668 h1:Y6B67gtYwMpePlI9Spz1EKVsLiAvKJ+rxRBZRWbGjmI= +maunium.net/go/mautrix v0.26.1-0.20251128114054-1d1ecb228668/go.mod h1:NaesYcOQWFDbixVYywCVS+Twlzab9hOUpFNlCBlvciE= From fcb1710cd8d5f7a908da36da8eccd556912226b7 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 1 Dec 2025 15:37:00 +0200 Subject: [PATCH 062/153] dependencies: update whatsmeow --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 875c93c..76e976b 100644 --- a/go.mod +++ b/go.mod @@ -11,13 +11,13 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.4-0.20251128195053-c7fab4d88a04 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20251127132918-b9ac3d51d746 + go.mau.fi/whatsmeow v0.0.0-20251201133539-d5bb5361b3d7 golang.org/x/image v0.33.0 golang.org/x/net v0.47.0 golang.org/x/sync v0.18.0 google.golang.org/protobuf v1.36.10 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.26.1-0.20251128114054-1d1ecb228668 + maunium.net/go/mautrix v0.26.1-0.20251201132856-09052986b2d3 ) require ( diff --git a/go.sum b/go.sum index 6141c5a..f2a0637 100644 --- a/go.sum +++ b/go.sum @@ -79,8 +79,8 @@ go.mau.fi/util v0.9.4-0.20251128195053-c7fab4d88a04 h1:1xsqu1gqwLUBEolvrsURhj/A3 go.mau.fi/util v0.9.4-0.20251128195053-c7fab4d88a04/go.mod h1:viDmhBOAFfcqDdKSk53EPJV3N4Mi8Jst5/ahGJ/vwsA= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20251127132918-b9ac3d51d746 h1:51hAK0a+KA4BD7MgqfVd6dbJd5cJBhotOlhWUybYCn0= -go.mau.fi/whatsmeow v0.0.0-20251127132918-b9ac3d51d746/go.mod h1:5aYaEa3FF5e5XWsA8Xa80ttUXZvb6HyaBGgo2SfzUkE= +go.mau.fi/whatsmeow v0.0.0-20251201133539-d5bb5361b3d7 h1:9vh1AMoNAR6YFJI2jSgCaTCqJMkgDf2wo02xayIL6l8= +go.mau.fi/whatsmeow v0.0.0-20251201133539-d5bb5361b3d7/go.mod h1:5aYaEa3FF5e5XWsA8Xa80ttUXZvb6HyaBGgo2SfzUkE= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= @@ -113,5 +113,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.26.1-0.20251128114054-1d1ecb228668 h1:Y6B67gtYwMpePlI9Spz1EKVsLiAvKJ+rxRBZRWbGjmI= -maunium.net/go/mautrix v0.26.1-0.20251128114054-1d1ecb228668/go.mod h1:NaesYcOQWFDbixVYywCVS+Twlzab9hOUpFNlCBlvciE= +maunium.net/go/mautrix v0.26.1-0.20251201132856-09052986b2d3 h1:2hzPLYsZbDY4MykMYS0xGjKYMXnlbAsznFkIHVp55Fo= +maunium.net/go/mautrix v0.26.1-0.20251201132856-09052986b2d3/go.mod h1:NaesYcOQWFDbixVYywCVS+Twlzab9hOUpFNlCBlvciE= From 23ea890b47fd5ed62e9374a87711fe2d077a3b80 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 2 Dec 2025 15:27:34 +0200 Subject: [PATCH 063/153] handlematrix: use new interface for redirecting invites to correct ID --- go.mod | 2 +- go.sum | 4 ++-- pkg/connector/handlematrix.go | 27 +++++++++++++++++---------- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/go.mod b/go.mod index 76e976b..eb8de66 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( golang.org/x/sync v0.18.0 google.golang.org/protobuf v1.36.10 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.26.1-0.20251201132856-09052986b2d3 + maunium.net/go/mautrix v0.26.1-0.20251202132204-2eeece694254 ) require ( diff --git a/go.sum b/go.sum index f2a0637..d95748e 100644 --- a/go.sum +++ b/go.sum @@ -113,5 +113,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.26.1-0.20251201132856-09052986b2d3 h1:2hzPLYsZbDY4MykMYS0xGjKYMXnlbAsznFkIHVp55Fo= -maunium.net/go/mautrix v0.26.1-0.20251201132856-09052986b2d3/go.mod h1:NaesYcOQWFDbixVYywCVS+Twlzab9hOUpFNlCBlvciE= +maunium.net/go/mautrix v0.26.1-0.20251202132204-2eeece694254 h1:rsc4H0sZVban6xQeIS6err1YangUiK5WzVfTS6s03ms= +maunium.net/go/mautrix v0.26.1-0.20251202132204-2eeece694254/go.mod h1:NaesYcOQWFDbixVYywCVS+Twlzab9hOUpFNlCBlvciE= diff --git a/pkg/connector/handlematrix.go b/pkg/connector/handlematrix.go index 3abc38e..a057d76 100644 --- a/pkg/connector/handlematrix.go +++ b/pkg/connector/handlematrix.go @@ -390,18 +390,18 @@ func (wa *WhatsAppClient) HandleMatrixDisappearingTimer(ctx context.Context, msg return true, nil } -func (wa *WhatsAppClient) HandleMatrixMembership(ctx context.Context, msg *bridgev2.MatrixMembershipChange) (bool, error) { +func (wa *WhatsAppClient) HandleMatrixMembership(ctx context.Context, msg *bridgev2.MatrixMembershipChange) (*bridgev2.MatrixMembershipResult, error) { portalJID, err := waid.ParsePortalID(msg.Portal.ID) if err != nil { - return false, err + return nil, err } if msg.Portal.RoomType == database.RoomTypeDM { switch msg.Type { case bridgev2.Invite: - return false, fmt.Errorf("cannot invite additional user to dm") + return nil, fmt.Errorf("cannot invite additional user to dm") default: - return false, nil + return nil, nil } } @@ -414,7 +414,7 @@ func (wa *WhatsAppClient) HandleMatrixMembership(ctx context.Context, msg *bridg case bridgev2.Leave, bridgev2.Kick: action = whatsmeow.ParticipantChangeRemove default: - return false, nil + return nil, nil } switch target := msg.Target.(type) { @@ -423,19 +423,26 @@ func (wa *WhatsAppClient) HandleMatrixMembership(ctx context.Context, msg *bridg case *bridgev2.UserLogin: ghost, err := target.Bridge.GetGhostByID(ctx, networkid.UserID(target.ID)) if err != nil { - return false, fmt.Errorf("failed to get ghost for user: %w", err) + return nil, fmt.Errorf("failed to get ghost for user: %w", err) } changes[0] = waid.ParseUserID(ghost.ID) default: - return false, fmt.Errorf("cannot get target intent: unknown type: %T", target) + return nil, fmt.Errorf("cannot get target intent: unknown type: %T", target) } - _, err = wa.Client.UpdateGroupParticipants(ctx, portalJID, changes, action) + resp, err := wa.Client.UpdateGroupParticipants(ctx, portalJID, changes, action) if err != nil { - return false, err + return nil, err + } else if len(resp) == 0 { + return nil, fmt.Errorf("no response for participant change") + } else if resp[0].Error != 0 { + return nil, fmt.Errorf("failed to change participant: code %d", resp[0].Error) } + zerolog.Ctx(ctx).Debug(). + Any("change_response", resp). + Msg("Handled membership change") - return true, nil + return &bridgev2.MatrixMembershipResult{RedirectTo: waid.MakeUserID(resp[0].JID)}, nil } func (wa *WhatsAppClient) HandleMatrixRoomName(ctx context.Context, msg *bridgev2.MatrixRoomName) (bool, error) { From 95a9a6233abadb040b0428efc1ef803d391f52b8 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 2 Dec 2025 15:48:56 +0200 Subject: [PATCH 064/153] dependencies: update whatsmeow --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index eb8de66..0502c75 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.4-0.20251128195053-c7fab4d88a04 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20251201133539-d5bb5361b3d7 + go.mau.fi/whatsmeow v0.0.0-20251202134806-b8b6014103aa golang.org/x/image v0.33.0 golang.org/x/net v0.47.0 golang.org/x/sync v0.18.0 diff --git a/go.sum b/go.sum index d95748e..e19ae59 100644 --- a/go.sum +++ b/go.sum @@ -79,8 +79,8 @@ go.mau.fi/util v0.9.4-0.20251128195053-c7fab4d88a04 h1:1xsqu1gqwLUBEolvrsURhj/A3 go.mau.fi/util v0.9.4-0.20251128195053-c7fab4d88a04/go.mod h1:viDmhBOAFfcqDdKSk53EPJV3N4Mi8Jst5/ahGJ/vwsA= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20251201133539-d5bb5361b3d7 h1:9vh1AMoNAR6YFJI2jSgCaTCqJMkgDf2wo02xayIL6l8= -go.mau.fi/whatsmeow v0.0.0-20251201133539-d5bb5361b3d7/go.mod h1:5aYaEa3FF5e5XWsA8Xa80ttUXZvb6HyaBGgo2SfzUkE= +go.mau.fi/whatsmeow v0.0.0-20251202134806-b8b6014103aa h1:eflj1+ZBVyerJ0drRo84+rkUmVvYZEFryt0Cjg0och8= +go.mau.fi/whatsmeow v0.0.0-20251202134806-b8b6014103aa/go.mod h1:5aYaEa3FF5e5XWsA8Xa80ttUXZvb6HyaBGgo2SfzUkE= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= From 8fb00ccc7c4127114b424258b00523b047921a14 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 3 Dec 2025 22:10:57 +0200 Subject: [PATCH 065/153] directmedia: update interface --- go.mod | 2 +- go.sum | 4 ++-- pkg/connector/directmedia.go | 33 +++++++++++++++++---------------- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/go.mod b/go.mod index 0502c75..c75f15f 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( golang.org/x/sync v0.18.0 google.golang.org/protobuf v1.36.10 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.26.1-0.20251202132204-2eeece694254 + maunium.net/go/mautrix v0.26.1-0.20251203195941-02ce6ff91851 ) require ( diff --git a/go.sum b/go.sum index e19ae59..9d3f352 100644 --- a/go.sum +++ b/go.sum @@ -113,5 +113,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.26.1-0.20251202132204-2eeece694254 h1:rsc4H0sZVban6xQeIS6err1YangUiK5WzVfTS6s03ms= -maunium.net/go/mautrix v0.26.1-0.20251202132204-2eeece694254/go.mod h1:NaesYcOQWFDbixVYywCVS+Twlzab9hOUpFNlCBlvciE= +maunium.net/go/mautrix v0.26.1-0.20251203195941-02ce6ff91851 h1:5dty5IkJGxpLj0SQ2+wwKIcrPfZML1uHFcGaQIA9te0= +maunium.net/go/mautrix v0.26.1-0.20251203195941-02ce6ff91851/go.mod h1:NaesYcOQWFDbixVYywCVS+Twlzab9hOUpFNlCBlvciE= diff --git a/pkg/connector/directmedia.go b/pkg/connector/directmedia.go index f91c28d..84d54db 100644 --- a/pkg/connector/directmedia.go +++ b/pkg/connector/directmedia.go @@ -127,12 +127,11 @@ func (wa *WhatsAppConnector) downloadAvatarDirectMedia(ctx context.Context, pars } } return &mediaproxy.GetMediaResponseFile{ - Callback: func(w *os.File) error { - return waClient.Client.DownloadMediaWithPathToFile( + Callback: func(w *os.File) (*mediaproxy.FileMeta, error) { + return &mediaproxy.FileMeta{}, waClient.Client.DownloadMediaWithPathToFile( ctx, cachedInfo.DirectPath, nil, nil, nil, 0, "", "", w, ) }, - ContentType: "", // TODO are avatars always jpeg? }, nil } @@ -176,18 +175,18 @@ func (wa *WhatsAppConnector) downloadMessageDirectMedia(ctx context.Context, par return nil, fmt.Errorf("no WhatsApp client found on login") } return &mediaproxy.GetMediaResponseFile{ - Callback: func(f *os.File) error { + Callback: func(f *os.File) (*mediaproxy.FileMeta, error) { err := waClient.Client.DownloadToFile(ctx, keys, f) if errors.Is(err, whatsmeow.ErrMediaDownloadFailedWith403) || errors.Is(err, whatsmeow.ErrMediaDownloadFailedWith404) || errors.Is(err, whatsmeow.ErrMediaDownloadFailedWith410) || errors.Is(err, whatsmeow.ErrNoURLPresent) { val := params["fi.mau.whatsapp.reload_media"] if val == "false" || (!wa.Config.DirectMediaAutoRequest && val != "true") { - return ErrReloadNeeded + return nil, ErrReloadNeeded } log.Trace().Msg("Media not found for direct download, requesting and waiting") err = waClient.requestAndWaitDirectMedia(ctx, msg.ID, keys) if err != nil { log.Trace().Err(err).Msg("Failed to wait for media for direct download") - return err + return nil, err } log.Trace().Msg("Retrying download after successful retry") err = waClient.Client.DownloadToFile(ctx, keys, f) @@ -195,27 +194,29 @@ func (wa *WhatsAppConnector) downloadMessageDirectMedia(ctx context.Context, par if errors.Is(err, whatsmeow.ErrFileLengthMismatch) || errors.Is(err, whatsmeow.ErrInvalidMediaSHA256) { zerolog.Ctx(ctx).Warn().Err(err).Msg("Mismatching media checksums in message. Ignoring because WhatsApp seems to ignore them too") } else if err != nil { - return err + return nil, err } - if keys.MimeType == "application/was" { + mime := keys.MimeType + if mime == "application/was" { if _, err := f.Seek(0, io.SeekStart); err != nil { - return fmt.Errorf("failed to seek to start of sticker zip: %w", err) + return nil, fmt.Errorf("failed to seek to start of sticker zip: %w", err) } else if zipData, err := io.ReadAll(f); err != nil { - return fmt.Errorf("failed to read sticker zip: %w", err) + return nil, fmt.Errorf("failed to read sticker zip: %w", err) } else if data, err := msgconv.ExtractAnimatedSticker(zipData); err != nil { - return fmt.Errorf("failed to extract animated sticker: %w %x", err, zipData) + return nil, fmt.Errorf("failed to extract animated sticker: %w %x", err, zipData) } else if _, err := f.WriteAt(data, 0); err != nil { - return fmt.Errorf("failed to write animated sticker to file: %w", err) + return nil, fmt.Errorf("failed to write animated sticker to file: %w", err) } else if err := f.Truncate(int64(len(data))); err != nil { - return fmt.Errorf("failed to truncate animated sticker file: %w", err) + return nil, fmt.Errorf("failed to truncate animated sticker file: %w", err) } + mime = "video/lottie+json" } - return nil + return &mediaproxy.FileMeta{ + ContentType: mime, + }, nil }, - // TODO? - ContentType: "", }, nil } From dc48e38fbcfba87314d8be306ee3481eb7bc7b3e Mon Sep 17 00:00:00 2001 From: Radon Rosborough Date: Wed, 3 Dec 2025 14:15:08 -0800 Subject: [PATCH 066/153] dependencies: upgrade whatsmeow to add 5xx retries (#868) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index c75f15f..93f14b6 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.4-0.20251128195053-c7fab4d88a04 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20251202134806-b8b6014103aa + go.mau.fi/whatsmeow v0.0.0-20251203212742-364369929a75 golang.org/x/image v0.33.0 golang.org/x/net v0.47.0 golang.org/x/sync v0.18.0 diff --git a/go.sum b/go.sum index 9d3f352..06bbbdb 100644 --- a/go.sum +++ b/go.sum @@ -79,8 +79,8 @@ go.mau.fi/util v0.9.4-0.20251128195053-c7fab4d88a04 h1:1xsqu1gqwLUBEolvrsURhj/A3 go.mau.fi/util v0.9.4-0.20251128195053-c7fab4d88a04/go.mod h1:viDmhBOAFfcqDdKSk53EPJV3N4Mi8Jst5/ahGJ/vwsA= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20251202134806-b8b6014103aa h1:eflj1+ZBVyerJ0drRo84+rkUmVvYZEFryt0Cjg0och8= -go.mau.fi/whatsmeow v0.0.0-20251202134806-b8b6014103aa/go.mod h1:5aYaEa3FF5e5XWsA8Xa80ttUXZvb6HyaBGgo2SfzUkE= +go.mau.fi/whatsmeow v0.0.0-20251203212742-364369929a75 h1:SqZYh7ssKBuBtC6g9Mvj5i0roB34WpUWzLNXtQ9azUI= +go.mau.fi/whatsmeow v0.0.0-20251203212742-364369929a75/go.mod h1:5aYaEa3FF5e5XWsA8Xa80ttUXZvb6HyaBGgo2SfzUkE= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= From 82d3445ffdb7fb970d29226a4168564645561921 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 9 Dec 2025 12:42:07 +0200 Subject: [PATCH 067/153] dependencies: update mautrix-go --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 93f14b6..5ec0dae 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ tool go.mau.fi/util/cmd/maubuild require ( github.com/lib/pq v1.10.9 github.com/rs/zerolog v1.34.0 - go.mau.fi/util v0.9.4-0.20251128195053-c7fab4d88a04 + go.mau.fi/util v0.9.4-0.20251206205611-85e6fd6551e0 go.mau.fi/webp v0.2.0 go.mau.fi/whatsmeow v0.0.0-20251203212742-364369929a75 golang.org/x/image v0.33.0 @@ -17,7 +17,7 @@ require ( golang.org/x/sync v0.18.0 google.golang.org/protobuf v1.36.10 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.26.1-0.20251203195941-02ce6ff91851 + maunium.net/go/mautrix v0.26.1-0.20251208143302-e7a95b7f9732 ) require ( diff --git a/go.sum b/go.sum index 06bbbdb..ca2cb4b 100644 --- a/go.sum +++ b/go.sum @@ -75,8 +75,8 @@ github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA= github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= go.mau.fi/libsignal v0.2.1 h1:vRZG4EzTn70XY6Oh/pVKrQGuMHBkAWlGRC22/85m9L0= go.mau.fi/libsignal v0.2.1/go.mod h1:iVvjrHyfQqWajOUaMEsIfo3IqgVMrhWcPiiEzk7NgoU= -go.mau.fi/util v0.9.4-0.20251128195053-c7fab4d88a04 h1:1xsqu1gqwLUBEolvrsURhj/A3e9GACg95i8gITI0Be0= -go.mau.fi/util v0.9.4-0.20251128195053-c7fab4d88a04/go.mod h1:viDmhBOAFfcqDdKSk53EPJV3N4Mi8Jst5/ahGJ/vwsA= +go.mau.fi/util v0.9.4-0.20251206205611-85e6fd6551e0 h1:ESebxPGULuuxxcZigjcBFyyU62tiyY6ivtX17P4BkvY= +go.mau.fi/util v0.9.4-0.20251206205611-85e6fd6551e0/go.mod h1:viDmhBOAFfcqDdKSk53EPJV3N4Mi8Jst5/ahGJ/vwsA= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= go.mau.fi/whatsmeow v0.0.0-20251203212742-364369929a75 h1:SqZYh7ssKBuBtC6g9Mvj5i0roB34WpUWzLNXtQ9azUI= @@ -113,5 +113,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.26.1-0.20251203195941-02ce6ff91851 h1:5dty5IkJGxpLj0SQ2+wwKIcrPfZML1uHFcGaQIA9te0= -maunium.net/go/mautrix v0.26.1-0.20251203195941-02ce6ff91851/go.mod h1:NaesYcOQWFDbixVYywCVS+Twlzab9hOUpFNlCBlvciE= +maunium.net/go/mautrix v0.26.1-0.20251208143302-e7a95b7f9732 h1:k+wQVLO5IoVxP3Ouo8OSxs2zDIRBG9QTHlXVsHxN+wc= +maunium.net/go/mautrix v0.26.1-0.20251208143302-e7a95b7f9732/go.mod h1:pzwIT42s+BhBjEYovmcOt69VlNW2RkJ6pCyZjYQHKIc= From b5a1b1b1b1e4dd26f7155d4aa113fa3a3e682671 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 9 Dec 2025 12:42:19 +0200 Subject: [PATCH 068/153] client: add connecting bridge state --- pkg/connector/client.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/connector/client.go b/pkg/connector/client.go index 7c5c6e9..1b70718 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -192,6 +192,7 @@ func (wa *WhatsAppClient) Connect(ctx context.Context) { wa.UserLogin.BridgeState.Send(state) return } + wa.UserLogin.BridgeState.Send(status.BridgeState{StateEvent: status.StateConnecting}) wa.Main.firstClientConnectOnce.Do(wa.Main.onFirstClientConnect) if err := wa.Main.updateProxy(ctx, wa.Client, false); err != nil { zerolog.Ctx(ctx).Err(err).Msg("Failed to update proxy") @@ -243,6 +244,7 @@ func (wa *WhatsAppClient) ConnectBackground(ctx context.Context, params *bridgev if wa.Client == nil { return bridgev2.ErrNotLoggedIn } + wa.UserLogin.BridgeState.Send(status.BridgeState{StateEvent: status.StateConnecting}) wa.Client.BackgroundEventCtx = wa.UserLogin.Log.WithContext(wa.Main.Bridge.BackgroundCtx) ch := make(chan error, 1) wa.offlineSyncWaiter.Store(&ch) @@ -259,7 +261,7 @@ func (wa *WhatsAppClient) ConnectBackground(ctx context.Context, params *bridgev defer func() { wa.Client.GetClientPayload = nil }() - err := wa.Client.Connect() + err := wa.Client.ConnectContext(ctx) if err != nil { return err } From 499857ce8da5d977b9e74337937e1dbfcac8b2dd Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 9 Dec 2025 12:52:47 +0200 Subject: [PATCH 069/153] client: undo connecting state for background connections --- pkg/connector/client.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/connector/client.go b/pkg/connector/client.go index 1b70718..1ffe33e 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -244,7 +244,6 @@ func (wa *WhatsAppClient) ConnectBackground(ctx context.Context, params *bridgev if wa.Client == nil { return bridgev2.ErrNotLoggedIn } - wa.UserLogin.BridgeState.Send(status.BridgeState{StateEvent: status.StateConnecting}) wa.Client.BackgroundEventCtx = wa.UserLogin.Log.WithContext(wa.Main.Bridge.BackgroundCtx) ch := make(chan error, 1) wa.offlineSyncWaiter.Store(&ch) From f3a47772f3f6ee99d311b9636856cb69e35f115b Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 14 Dec 2025 14:39:08 +0200 Subject: [PATCH 070/153] docker: update to Alpine 3.23 --- Dockerfile | 4 ++-- Dockerfile.ci | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 3b71e4f..4efc9d5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1-alpine3.22 AS builder +FROM golang:1-alpine3.23 AS builder RUN apk add --no-cache git ca-certificates build-base su-exec olm-dev @@ -6,7 +6,7 @@ COPY . /build WORKDIR /build RUN ./build.sh -FROM alpine:3.22 +FROM alpine:3.23 ENV UID=1337 \ GID=1337 diff --git a/Dockerfile.ci b/Dockerfile.ci index 16ac07d..cb3be51 100644 --- a/Dockerfile.ci +++ b/Dockerfile.ci @@ -1,6 +1,6 @@ ARG DOCKER_HUB="docker.io" -FROM ${DOCKER_HUB}/alpine:3.22 +FROM ${DOCKER_HUB}/alpine:3.23 ENV UID=1337 \ GID=1337 From 34b2e2fb303c4667fc104f2a756e66c510c78ae3 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 15 Dec 2025 13:48:27 +0200 Subject: [PATCH 071/153] capabilities: remove incorrect edit max count --- pkg/connector/capabilities.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/connector/capabilities.go b/pkg/connector/capabilities.go index 75a47e9..202569b 100644 --- a/pkg/connector/capabilities.go +++ b/pkg/connector/capabilities.go @@ -51,7 +51,7 @@ func (wa *WhatsAppConnector) GetCapabilities() *bridgev2.NetworkGeneralCapabilit } func (wa *WhatsAppConnector) GetBridgeInfoVersion() (info, caps int) { - return 1, 6 + return 1, 7 } const WAMaxFileSize = 2000 * 1024 * 1024 @@ -66,7 +66,7 @@ func supportedIfFFmpeg() event.CapabilitySupportLevel { } func capID() string { - base := "fi.mau.whatsapp.capabilities.2025_10_27" + base := "fi.mau.whatsapp.capabilities.2025_12_15" if ffmpeg.Supported() { return base + "+ffmpeg" } @@ -178,7 +178,6 @@ var whatsappCaps = &event.RoomFeatures{ Poll: event.CapLevelFullySupported, Reply: event.CapLevelFullySupported, Edit: event.CapLevelFullySupported, - EditMaxCount: 10, EditMaxAge: ptr.Ptr(jsontime.S(EditMaxAge)), Delete: event.CapLevelFullySupported, DeleteForMe: false, From 713320ffb0d1d148e6a2b05dda7b0d8a3693a348 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 15 Dec 2025 13:48:40 +0200 Subject: [PATCH 072/153] changelog: update --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c199b1..1eaefb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# v25.12 (unreleased) + +* Updated Docker image to Alpine 3.23. +* Fixed group member invites from Matrix not automatically disinviting the phone + number ghost when the invite is redirected to a LID ghost. + # v25.11 * Added interface support for notifying about failed invites when creating a From 7faaf99896821d211b2b2fa023f119c2da925b56 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 15 Dec 2025 17:27:17 +0200 Subject: [PATCH 073/153] ci,dependencies: update --- .github/workflows/go.yml | 4 ++-- .github/workflows/stale.yml | 2 +- .pre-commit-config.yaml | 2 +- go.mod | 26 ++++++++++---------- go.sum | 48 ++++++++++++++++++------------------- 5 files changed, 41 insertions(+), 41 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index ae5f5aa..dbc573f 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -15,10 +15,10 @@ jobs: name: Lint ${{ matrix.go-version == '1.25' && '(latest)' || '(old)' }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Set up Go - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version: ${{ matrix.go-version }} cache: true diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 0ae50e4..cae8a4d 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -17,7 +17,7 @@ jobs: lock-stale: runs-on: ubuntu-latest steps: - - uses: dessant/lock-threads@v5 + - uses: dessant/lock-threads@v6 id: lock with: issue-inactive-days: 90 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a48f8b8..1bfb536 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ repos: - id: check-added-large-files - repo: https://github.com/tekwizely/pre-commit-golang - rev: v1.0.0-rc.2 + rev: v1.0.0-rc.4 hooks: - id: go-imports-repo args: diff --git a/go.mod b/go.mod index 5ec0dae..38c2257 100644 --- a/go.mod +++ b/go.mod @@ -2,22 +2,22 @@ module go.mau.fi/mautrix-whatsapp go 1.24.0 -toolchain go1.25.4 +toolchain go1.25.5 tool go.mau.fi/util/cmd/maubuild require ( github.com/lib/pq v1.10.9 github.com/rs/zerolog v1.34.0 - go.mau.fi/util v0.9.4-0.20251206205611-85e6fd6551e0 + go.mau.fi/util v0.9.4-0.20251214123745-64997f646f1d go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20251203212742-364369929a75 - golang.org/x/image v0.33.0 - golang.org/x/net v0.47.0 - golang.org/x/sync v0.18.0 - google.golang.org/protobuf v1.36.10 + go.mau.fi/whatsmeow v0.0.0-20251215152620-b553db2f6bb9 + golang.org/x/image v0.34.0 + golang.org/x/net v0.48.0 + golang.org/x/sync v0.19.0 + google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.26.1-0.20251208143302-e7a95b7f9732 + maunium.net/go/mautrix v0.26.1-0.20251214123757-4be256229706 ) require ( @@ -43,11 +43,11 @@ require ( github.com/yuin/goldmark v1.7.13 // indirect go.mau.fi/libsignal v0.2.1 // indirect go.mau.fi/zeroconfig v0.2.0 // indirect - golang.org/x/crypto v0.45.0 // indirect - golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 // indirect - golang.org/x/mod v0.30.0 // indirect - golang.org/x/sys v0.38.0 // indirect - golang.org/x/text v0.31.0 // indirect + golang.org/x/crypto v0.46.0 // indirect + golang.org/x/exp v0.0.0-20251209150349-8475f28825e9 // indirect + golang.org/x/mod v0.31.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.org/x/text v0.32.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect maunium.net/go/mauflag v1.0.0 // indirect diff --git a/go.sum b/go.sum index ca2cb4b..b0423a4 100644 --- a/go.sum +++ b/go.sum @@ -75,35 +75,35 @@ github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA= github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= go.mau.fi/libsignal v0.2.1 h1:vRZG4EzTn70XY6Oh/pVKrQGuMHBkAWlGRC22/85m9L0= go.mau.fi/libsignal v0.2.1/go.mod h1:iVvjrHyfQqWajOUaMEsIfo3IqgVMrhWcPiiEzk7NgoU= -go.mau.fi/util v0.9.4-0.20251206205611-85e6fd6551e0 h1:ESebxPGULuuxxcZigjcBFyyU62tiyY6ivtX17P4BkvY= -go.mau.fi/util v0.9.4-0.20251206205611-85e6fd6551e0/go.mod h1:viDmhBOAFfcqDdKSk53EPJV3N4Mi8Jst5/ahGJ/vwsA= +go.mau.fi/util v0.9.4-0.20251214123745-64997f646f1d h1:/w7U+vxbtIYEhMHZaXxYqTU1HmO6kJK3BxnG66Rzk3o= +go.mau.fi/util v0.9.4-0.20251214123745-64997f646f1d/go.mod h1:OwI76F1QINxtH/TOydGAAj5/VvtPG0RnZzB41rtnKcA= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20251203212742-364369929a75 h1:SqZYh7ssKBuBtC6g9Mvj5i0roB34WpUWzLNXtQ9azUI= -go.mau.fi/whatsmeow v0.0.0-20251203212742-364369929a75/go.mod h1:5aYaEa3FF5e5XWsA8Xa80ttUXZvb6HyaBGgo2SfzUkE= +go.mau.fi/whatsmeow v0.0.0-20251215152620-b553db2f6bb9 h1:vcQ9vuWiwSHBPdwgEyuqMDgHcCdRC+p1aOAIx6j2eVY= +go.mau.fi/whatsmeow v0.0.0-20251215152620-b553db2f6bb9/go.mod h1:5aYaEa3FF5e5XWsA8Xa80ttUXZvb6HyaBGgo2SfzUkE= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= -golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= -golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= -golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 h1:DHNhtq3sNNzrvduZZIiFyXWOL9IWaDPHqTnLJp+rCBY= -golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0= -golang.org/x/image v0.33.0 h1:LXRZRnv1+zGd5XBUVRFmYEphyyKJjQjCRiOuAP3sZfQ= -golang.org/x/image v0.33.0/go.mod h1:DD3OsTYT9chzuzTQt+zMcOlBHgfoKQb1gry8p76Y1sc= -golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk= -golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc= -golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= -golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= +golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= +golang.org/x/exp v0.0.0-20251209150349-8475f28825e9 h1:MDfG8Cvcqlt9XXrmEiD4epKn7VJHZO84hejP9Jmp0MM= +golang.org/x/exp v0.0.0-20251209150349-8475f28825e9/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU= +golang.org/x/image v0.34.0 h1:33gCkyw9hmwbZJeZkct8XyR11yH889EQt/QH4VmXMn8= +golang.org/x/image v0.34.0/go.mod h1:2RNFBZRB+vnwwFil8GkMdRvrJOFd1AzdZI6vOY+eJVU= +golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= +golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= +golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= +golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= -google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= -google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= @@ -113,5 +113,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.26.1-0.20251208143302-e7a95b7f9732 h1:k+wQVLO5IoVxP3Ouo8OSxs2zDIRBG9QTHlXVsHxN+wc= -maunium.net/go/mautrix v0.26.1-0.20251208143302-e7a95b7f9732/go.mod h1:pzwIT42s+BhBjEYovmcOt69VlNW2RkJ6pCyZjYQHKIc= +maunium.net/go/mautrix v0.26.1-0.20251214123757-4be256229706 h1:i0C+KohzQsz1WzhF3TSCA3pSGl7k2MQZ5kNFpUllWrg= +maunium.net/go/mautrix v0.26.1-0.20251214123757-4be256229706/go.mod h1:tMXkJiGf+2dEPvfrmhQVD6jReL5E8jI1o2dPSsy/39Y= From 1f06bd38cdf69123f86fd5ab3042b958a197a137 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 16 Dec 2025 12:34:49 +0200 Subject: [PATCH 074/153] Bump version to v25.12 --- CHANGELOG.md | 2 +- cmd/mautrix-whatsapp/main.go | 2 +- go.mod | 8 ++++---- go.sum | 15 ++++++++------- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1eaefb3..62c06b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# v25.12 (unreleased) +# v25.12 * Updated Docker image to Alpine 3.23. * Fixed group member invites from Matrix not automatically disinviting the phone diff --git a/cmd/mautrix-whatsapp/main.go b/cmd/mautrix-whatsapp/main.go index b71912f..6750c3a 100644 --- a/cmd/mautrix-whatsapp/main.go +++ b/cmd/mautrix-whatsapp/main.go @@ -18,7 +18,7 @@ var m = mxmain.BridgeMain{ Name: "mautrix-whatsapp", URL: "https://github.com/mautrix/whatsapp", Description: "A Matrix-WhatsApp puppeting bridge.", - Version: "25.11", + Version: "25.12", SemCalVer: true, Connector: &connector.WhatsAppConnector{}, } diff --git a/go.mod b/go.mod index 38c2257..01bb375 100644 --- a/go.mod +++ b/go.mod @@ -9,22 +9,22 @@ tool go.mau.fi/util/cmd/maubuild require ( github.com/lib/pq v1.10.9 github.com/rs/zerolog v1.34.0 - go.mau.fi/util v0.9.4-0.20251214123745-64997f646f1d + go.mau.fi/util v0.9.4 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20251215152620-b553db2f6bb9 + go.mau.fi/whatsmeow v0.0.0-20251216102424-56a8e44b0cec golang.org/x/image v0.34.0 golang.org/x/net v0.48.0 golang.org/x/sync v0.19.0 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.26.1-0.20251214123757-4be256229706 + maunium.net/go/mautrix v0.26.1 ) require ( filippo.io/edwards25519 v1.1.0 // indirect github.com/beeper/argo-go v1.1.2 // indirect github.com/coder/websocket v1.8.14 // indirect - github.com/coreos/go-systemd/v22 v22.5.0 // indirect + github.com/coreos/go-systemd/v22 v22.6.0 // indirect github.com/elliotchance/orderedmap/v3 v3.1.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/kr/pretty v0.3.1 // indirect diff --git a/go.sum b/go.sum index b0423a4..9a0bb67 100644 --- a/go.sum +++ b/go.sum @@ -10,8 +10,9 @@ github.com/beeper/argo-go v1.1.2 h1:UQI2G8F+NLfGTOmTUI0254pGKx/HUU/etbUGTJv91Fs= github.com/beeper/argo-go v1.1.2/go.mod h1:M+LJAnyowKVQ6Rdj6XYGEn+qcVFkb3R/MUpqkGR0hM4= github.com/coder/websocket v1.8.14 h1:9L0p0iKiNOibykf283eHkKUHHrpG7f65OE3BhhO7v9g= github.com/coder/websocket v1.8.14/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg= -github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/go-systemd/v22 v22.6.0 h1:aGVa/v8B7hpb0TKl0MWoAavPDmHvobFe5R5zn0bCJWo= +github.com/coreos/go-systemd/v22 v22.6.0/go.mod h1:iG+pp635Fo7ZmV/j14KUcmEyWF+0X7Lua8rrTWzYgWU= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -75,12 +76,12 @@ github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA= github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= go.mau.fi/libsignal v0.2.1 h1:vRZG4EzTn70XY6Oh/pVKrQGuMHBkAWlGRC22/85m9L0= go.mau.fi/libsignal v0.2.1/go.mod h1:iVvjrHyfQqWajOUaMEsIfo3IqgVMrhWcPiiEzk7NgoU= -go.mau.fi/util v0.9.4-0.20251214123745-64997f646f1d h1:/w7U+vxbtIYEhMHZaXxYqTU1HmO6kJK3BxnG66Rzk3o= -go.mau.fi/util v0.9.4-0.20251214123745-64997f646f1d/go.mod h1:OwI76F1QINxtH/TOydGAAj5/VvtPG0RnZzB41rtnKcA= +go.mau.fi/util v0.9.4 h1:gWdUff+K2rCynRPysXalqqQyr2ahkSWaestH6YhSpso= +go.mau.fi/util v0.9.4/go.mod h1:647nVfwUvuhlZFOnro3aRNPmRd2y3iDha9USb8aKSmM= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20251215152620-b553db2f6bb9 h1:vcQ9vuWiwSHBPdwgEyuqMDgHcCdRC+p1aOAIx6j2eVY= -go.mau.fi/whatsmeow v0.0.0-20251215152620-b553db2f6bb9/go.mod h1:5aYaEa3FF5e5XWsA8Xa80ttUXZvb6HyaBGgo2SfzUkE= +go.mau.fi/whatsmeow v0.0.0-20251216102424-56a8e44b0cec h1:pNU+bpMGDodCJt6ufF1ImhuuBgzut4kaHklU7tey2g0= +go.mau.fi/whatsmeow v0.0.0-20251216102424-56a8e44b0cec/go.mod h1:S4OWR9+hTx+54+jRzl+NfRBXnGpPm5IRPyhXB7haSd0= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= @@ -113,5 +114,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.26.1-0.20251214123757-4be256229706 h1:i0C+KohzQsz1WzhF3TSCA3pSGl7k2MQZ5kNFpUllWrg= -maunium.net/go/mautrix v0.26.1-0.20251214123757-4be256229706/go.mod h1:tMXkJiGf+2dEPvfrmhQVD6jReL5E8jI1o2dPSsy/39Y= +maunium.net/go/mautrix v0.26.1 h1:FWCC1xY5vwJ5ou3duEBjB6w9IIlwfc9el3q3Mju3Dlg= +maunium.net/go/mautrix v0.26.1/go.mod h1:UySSpb8OqXG1sMJ6dDqyzmfcqr2ayZK+KzwqOTAkAOM= From c050d3eb345d6feb464e185557198290a3f393cb Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 17 Dec 2025 16:36:29 +0200 Subject: [PATCH 075/153] connector: add timeout for fetching latest whatsapp web version --- go.mod | 2 +- go.sum | 4 ++-- pkg/connector/client.go | 1 + pkg/connector/connector.go | 14 +++++++++++++- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 01bb375..7502038 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.4 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20251216102424-56a8e44b0cec + go.mau.fi/whatsmeow v0.0.0-20251217143725-11cf47c62d32 golang.org/x/image v0.34.0 golang.org/x/net v0.48.0 golang.org/x/sync v0.19.0 diff --git a/go.sum b/go.sum index 9a0bb67..79dd3ee 100644 --- a/go.sum +++ b/go.sum @@ -80,8 +80,8 @@ go.mau.fi/util v0.9.4 h1:gWdUff+K2rCynRPysXalqqQyr2ahkSWaestH6YhSpso= go.mau.fi/util v0.9.4/go.mod h1:647nVfwUvuhlZFOnro3aRNPmRd2y3iDha9USb8aKSmM= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20251216102424-56a8e44b0cec h1:pNU+bpMGDodCJt6ufF1ImhuuBgzut4kaHklU7tey2g0= -go.mau.fi/whatsmeow v0.0.0-20251216102424-56a8e44b0cec/go.mod h1:S4OWR9+hTx+54+jRzl+NfRBXnGpPm5IRPyhXB7haSd0= +go.mau.fi/whatsmeow v0.0.0-20251217143725-11cf47c62d32 h1:NeE9eEYY4kEJVCfCXaAU27LgAPugPHRHJdC9IpXFPzI= +go.mau.fi/whatsmeow v0.0.0-20251217143725-11cf47c62d32/go.mod h1:S4OWR9+hTx+54+jRzl+NfRBXnGpPm5IRPyhXB7haSd0= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= diff --git a/pkg/connector/client.go b/pkg/connector/client.go index 1ffe33e..2eecf04 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -202,6 +202,7 @@ func (wa *WhatsAppClient) Connect(ctx context.Context) { } wa.startLoops() wa.Client.BackgroundEventCtx = wa.UserLogin.Log.WithContext(wa.Main.Bridge.BackgroundCtx) + zerolog.Ctx(ctx).Debug().Msg("Connecting to WhatsApp") if err := wa.Client.ConnectContext(ctx); err != nil { zerolog.Ctx(ctx).Err(err).Msg("Failed to connect to WhatsApp") state := status.BridgeState{ diff --git a/pkg/connector/connector.go b/pkg/connector/connector.go index af23ed4..1b7fc68 100644 --- a/pkg/connector/connector.go +++ b/pkg/connector/connector.go @@ -20,9 +20,12 @@ import ( "context" "encoding/hex" "fmt" + "net" + "net/http" "strings" "sync" "sync/atomic" + "time" "github.com/lib/pq" "github.com/rs/zerolog" @@ -256,8 +259,17 @@ func (wa *WhatsAppConnector) onFirstBackgroundConnect() { } func (wa *WhatsAppConnector) onFirstClientConnect() { + wa.Bridge.Log.Debug().Msg("Fetching latest WhatsApp web version number") ctx := wa.Bridge.BackgroundCtx - ver, err := whatsmeow.GetLatestVersion(ctx, nil) + ver, err := whatsmeow.GetLatestVersion(ctx, &http.Client{ + Transport: &http.Transport{ + DialContext: (&net.Dialer{Timeout: 5 * time.Second}).DialContext, + TLSHandshakeTimeout: 5 * time.Second, + ResponseHeaderTimeout: 5 * time.Second, + ForceAttemptHTTP2: true, + }, + Timeout: 10 * time.Second, + }) if err != nil { wa.Bridge.Log.Err(err).Msg("Failed to get latest WhatsApp web version number") } else { From 1214b97c656eb48c006ef70489d67ad1a904b416 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 18 Dec 2025 14:35:35 +0200 Subject: [PATCH 076/153] handlematrix: leave group before deleting chat (#870) --- pkg/connector/handlematrix.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pkg/connector/handlematrix.go b/pkg/connector/handlematrix.go index a057d76..ce8b7b2 100644 --- a/pkg/connector/handlematrix.go +++ b/pkg/connector/handlematrix.go @@ -647,6 +647,18 @@ func (wa *WhatsAppClient) HandleMatrixDeleteChat(ctx context.Context, msg *bridg if err != nil { return err } + if chatJID.Server == types.GroupServer { + memberInfo, err := wa.Main.Bridge.Matrix.GetMemberInfo(ctx, msg.Portal.MXID, wa.UserLogin.UserMXID) + if err != nil { + return fmt.Errorf("failed to get own member info: %w", err) + } else if memberInfo.Membership == event.MembershipJoin { + err = wa.Client.LeaveGroup(ctx, chatJID) + if err != nil { + // TODO ignore errors saying you already left the group? + return fmt.Errorf("failed to leave group before deleting chat: %w", err) + } + } + } lastTS, lastKey, err := wa.getLastMessageInfo(ctx, chatJID, msg.Portal.PortalKey) if err != nil { return err From c39f5eb9cb17fa3fad57184a5c5ba6af4323274c Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 30 Dec 2025 23:01:00 +0200 Subject: [PATCH 077/153] startchat,handlematrix: fix handling room avatars --- pkg/connector/handlematrix.go | 4 ++-- pkg/connector/startchat.go | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pkg/connector/handlematrix.go b/pkg/connector/handlematrix.go index ce8b7b2..5d51d12 100644 --- a/pkg/connector/handlematrix.go +++ b/pkg/connector/handlematrix.go @@ -501,8 +501,8 @@ func (wa *WhatsAppClient) HandleMatrixRoomAvatar(ctx context.Context, msg *bridg } var data []byte - if msg.Content.URL != "" || msg.Content.MSC3414File != nil { - data, err = msg.Portal.Bridge.Bot.DownloadMedia(ctx, msg.Content.URL, msg.Content.MSC3414File) + if msg.Content.URL != "" { + data, err = msg.Portal.Bridge.Bot.DownloadMedia(ctx, msg.Content.URL, nil) if err != nil { return false, fmt.Errorf("failed to download avatar: %w", err) } diff --git a/pkg/connector/startchat.go b/pkg/connector/startchat.go index 22d4ad8..aa468cb 100644 --- a/pkg/connector/startchat.go +++ b/pkg/connector/startchat.go @@ -240,10 +240,10 @@ func (wa *WhatsAppClient) CreateGroup(ctx context.Context, params *bridgev2.Grou } var avatarBytes []byte var avatarMXC id.ContentURIString - if params.Avatar != nil { + if params.Avatar != nil && params.Avatar.URL != "" { avatarMXC = params.Avatar.URL var err error - avatarBytes, err = wa.Main.Bridge.Bot.DownloadMedia(ctx, params.Avatar.URL, params.Avatar.MSC3414File) + avatarBytes, err = wa.Main.Bridge.Bot.DownloadMedia(ctx, params.Avatar.URL, nil) if err != nil { return nil, fmt.Errorf("failed to download avatar: %w", err) } @@ -331,6 +331,7 @@ func (wa *WhatsAppClient) CreateGroup(ctx context.Context, params *bridgev2.Grou portal.AvatarID = networkid.AvatarID(avatarID) portal.AvatarHash = sha256.Sum256(avatarBytes) portal.AvatarMXC = avatarMXC + portal.AvatarSet = true groupInfo.Avatar = &bridgev2.Avatar{ ID: portal.AvatarID, MXC: portal.AvatarMXC, From 59bc54e60f39151802d47ef2d9349b5a5f8c7907 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 7 Jan 2026 13:38:22 +0200 Subject: [PATCH 078/153] handlewhatsapp: reroute LID senders in broadcast lists --- pkg/connector/handlewhatsapp.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index a542694..37a355c 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -252,7 +252,8 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) (success bool) { } func (wa *WhatsAppClient) rerouteWAMessage(ctx context.Context, evtType string, info *types.MessageSource, msgID any) { - if info.Chat.Server == types.HiddenUserServer && info.SenderAlt.IsEmpty() { + if (info.Chat.Server == types.HiddenUserServer || info.Chat.Server == types.BroadcastServer) && + info.Sender.Server == types.HiddenUserServer && info.SenderAlt.IsEmpty() { info.SenderAlt, _ = wa.GetStore().LIDs.GetPNForLID(ctx, info.Sender) } if info.Chat.Server == types.HiddenUserServer && info.Sender.ToNonAD() == info.Chat && info.SenderAlt.Server == types.DefaultUserServer { @@ -279,6 +280,15 @@ func (wa *WhatsAppClient) rerouteWAMessage(ctx context.Context, evtType string, info.Sender.Device = info.SenderAlt.Device } } + } else if info.Chat.Server == types.BroadcastServer && info.Sender.Server == types.HiddenUserServer && info.SenderAlt.Server == types.DefaultUserServer { + wa.UserLogin.Log.Debug(). + Stringer("lid", info.Sender). + Stringer("pn", info.SenderAlt). + Stringer("chat", info.Chat). + Any("message_id", msgID). + Str("evt_type", evtType). + Msg("Forced LID broadcast list sender to phone number in incoming message") + info.Sender, info.SenderAlt = info.SenderAlt, info.Sender } else if info.Sender.Server == types.BotServer && info.Chat.Server == types.HiddenUserServer { chatPN, err := wa.GetStore().LIDs.GetPNForLID(ctx, info.Chat) if err != nil { From 0a0d72e7435691d58c9e5e719d099ee4fa17805c Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 7 Jan 2026 14:53:26 +0200 Subject: [PATCH 079/153] backfill: include message count in all history sync storage logs --- pkg/connector/backfill.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/pkg/connector/backfill.go b/pkg/connector/backfill.go index c9aa521..f05c0f2 100644 --- a/pkg/connector/backfill.go +++ b/pkg/connector/backfill.go @@ -181,17 +181,21 @@ func (wa *WhatsAppClient) handleWAHistorySync(ctx context.Context, evt *waHistor failedToSaveTotal := 0 totalMessageCount := 0 for _, conv := range evt.GetConversations() { + log := log.With(). + Int("msg_count", len(conv.GetMessages())). + Logger() jid, err := types.ParseJID(conv.GetID()) if err != nil { totalMessageCount += len(conv.GetMessages()) log.Warn().Err(err). Str("chat_jid", conv.GetID()). - Int("msg_count", len(conv.GetMessages())). Msg("Failed to parse chat JID in history sync") continue } else if jid.Server == types.BroadcastServer { log.Debug().Stringer("chat_jid", jid).Msg("Skipping broadcast list in history sync") continue + } else { + totalMessageCount += len(conv.GetMessages()) } if jid.Server == types.HiddenUserServer { pn, err := wa.GetStore().LIDs.GetPNForLID(ctx, jid) @@ -207,11 +211,9 @@ func (wa *WhatsAppClient) handleWAHistorySync(ctx context.Context, evt *waHistor jid = pn } } - totalMessageCount += len(conv.GetMessages()) - log := log.With(). - Stringer("chat_jid", jid). - Int("msg_count", len(conv.GetMessages())). - Logger() + log.UpdateContext(func(c zerolog.Context) zerolog.Context { + return c.Stringer("chat_jid", jid) + }) var minTime, maxTime time.Time var minTimeIndex, maxTimeIndex int From 360564585689696ba19d99500ac13138cb205205 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 7 Jan 2026 14:53:35 +0200 Subject: [PATCH 080/153] client: always use manual history sync downloads --- pkg/connector/client.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pkg/connector/client.go b/pkg/connector/client.go index 2eecf04..2556a14 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -74,10 +74,8 @@ func (wa *WhatsAppConnector) LoadUserLogin(ctx context.Context, login *bridgev2. w.Client = whatsmeow.NewClient(w.Device, waLog.Zerolog(log)) w.Client.AddEventHandlerWithSuccessStatus(w.handleWAEvent) w.Client.SynchronousAck = true - if bridgev2.PortalEventBuffer == 0 { - w.Client.EnableDecryptedEventBuffer = true - w.Client.ManualHistorySyncDownload = true - } + w.Client.EnableDecryptedEventBuffer = bridgev2.PortalEventBuffer == 0 + w.Client.ManualHistorySyncDownload = true w.Client.SendReportingTokens = true w.Client.AutomaticMessageRerequestFromPhone = true w.Client.GetMessageForRetry = w.trackNotFoundRetry From 0c5704fc4d31e70c4de2376a680fc09881bdbc98 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 7 Jan 2026 15:03:45 +0200 Subject: [PATCH 081/153] wadb: rerun LID DM migration --- pkg/connector/wadb/upgrades/00-latest-schema.sql | 2 +- .../wadb/upgrades/09-may-need-lid-dm-deletion-again.sql | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 pkg/connector/wadb/upgrades/09-may-need-lid-dm-deletion-again.sql diff --git a/pkg/connector/wadb/upgrades/00-latest-schema.sql b/pkg/connector/wadb/upgrades/00-latest-schema.sql index d9aeac9..850f5b4 100644 --- a/pkg/connector/wadb/upgrades/00-latest-schema.sql +++ b/pkg/connector/wadb/upgrades/00-latest-schema.sql @@ -1,4 +1,4 @@ --- v0 -> v8 (compatible with v3+): Latest revision +-- v0 -> v9 (compatible with v3+): Latest revision CREATE TABLE whatsapp_poll_option_id ( bridge_id TEXT NOT NULL, diff --git a/pkg/connector/wadb/upgrades/09-may-need-lid-dm-deletion-again.sql b/pkg/connector/wadb/upgrades/09-may-need-lid-dm-deletion-again.sql new file mode 100644 index 0000000..5c32e65 --- /dev/null +++ b/pkg/connector/wadb/upgrades/09-may-need-lid-dm-deletion-again.sql @@ -0,0 +1,3 @@ +-- v9 (compatible with v3+): Mark LID DMs for deletion (again) +DELETE FROM kv_store WHERE bridge_id='' AND key='whatsapp_lid_dms_deleted'; +INSERT INTO kv_store (bridge_id, key, value) VALUES ('', 'whatsapp_lid_dms_deleted', 'false'); From 7e9cf12b4f266f307ef37864653bc33b4ba9e1a1 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 7 Jan 2026 20:01:19 +0200 Subject: [PATCH 082/153] handlematrix: remove unnecessary check when deleting chats --- pkg/connector/handlematrix.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/pkg/connector/handlematrix.go b/pkg/connector/handlematrix.go index 5d51d12..7282c7c 100644 --- a/pkg/connector/handlematrix.go +++ b/pkg/connector/handlematrix.go @@ -663,8 +663,5 @@ func (wa *WhatsAppClient) HandleMatrixDeleteChat(ctx context.Context, msg *bridg if err != nil { return err } - if lastKey == nil { - return fmt.Errorf("failed to delete chat: no messages found") - } return wa.Client.SendAppState(ctx, appstate.BuildDeleteChat(chatJID, lastTS, lastKey)) } From e3351ce5b0915a8aed8c26165f371329ffd53a8b Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 7 Jan 2026 20:54:06 +0200 Subject: [PATCH 083/153] handlewhatsapp: fix rerouting receipts in LID DMs --- pkg/connector/handlewhatsapp.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index 37a355c..2ccd936 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -256,6 +256,9 @@ func (wa *WhatsAppClient) rerouteWAMessage(ctx context.Context, evtType string, info.Sender.Server == types.HiddenUserServer && info.SenderAlt.IsEmpty() { info.SenderAlt, _ = wa.GetStore().LIDs.GetPNForLID(ctx, info.Sender) } + if info.Chat.Server == types.HiddenUserServer && info.IsFromMe && info.RecipientAlt.IsEmpty() { + info.RecipientAlt, _ = wa.GetStore().LIDs.GetPNForLID(ctx, info.Chat) + } if info.Chat.Server == types.HiddenUserServer && info.Sender.ToNonAD() == info.Chat && info.SenderAlt.Server == types.DefaultUserServer { wa.UserLogin.Log.Debug(). Stringer("lid", info.Sender). @@ -423,6 +426,7 @@ func (wa *WhatsAppClient) handleWAUndecryptableMessage(ctx context.Context, evt } func (wa *WhatsAppClient) handleWAReceipt(ctx context.Context, evt *events.Receipt) (success bool) { + origChat := evt.Chat wa.rerouteWAMessage(ctx, "receipt", &evt.MessageSource, evt.MessageIDs) if evt.IsFromMe && evt.Sender.Device == 0 { wa.phoneSeen(evt.Timestamp) @@ -442,6 +446,10 @@ func (wa *WhatsAppClient) handleWAReceipt(ctx context.Context, evt *events.Recei messageSender := wa.JID if !evt.MessageSender.IsEmpty() { messageSender = evt.MessageSender + // Second part of rerouting receipts in LID chats + if messageSender == origChat && evt.Chat != origChat { + messageSender = evt.Chat + } } else if evt.Chat.Server == types.GroupServer && evt.Sender.Server == types.HiddenUserServer { lid := wa.GetStore().GetLID() if !lid.IsEmpty() { @@ -613,6 +621,7 @@ func (wa *WhatsAppClient) handleWADeleteChat(evt *events.DeleteChat) bool { } func (wa *WhatsAppClient) handleWADeleteForMe(evt *events.DeleteForMe) bool { + // FIXME this doesn't handle IsFromMe properly, might also need LID fixes return wa.UserLogin.QueueRemoteEvent(&simplevent.MessageRemove{ EventMeta: simplevent.EventMeta{ Type: bridgev2.RemoteEventMessageRemove, From 881dce121a5ed682d94b58ecfe8e3fcad698d76c Mon Sep 17 00:00:00 2001 From: Nick Mills-Barrett Date: Thu, 8 Jan 2026 16:34:38 +0000 Subject: [PATCH 084/153] directmedia: improve error messages after requesting media --- pkg/connector/directmedia.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pkg/connector/directmedia.go b/pkg/connector/directmedia.go index 84d54db..c085d41 100644 --- a/pkg/connector/directmedia.go +++ b/pkg/connector/directmedia.go @@ -254,12 +254,16 @@ func (wa *WhatsAppClient) requestAndWaitDirectMedia(ctx context.Context, rawMsgI } switch state.resultType { case waMmsRetry.MediaRetryNotification_NOT_FOUND: - return mautrix.MNotFound.WithMessage("Media not found on phone") + return mautrix.MNotFound.WithMessage("This media was not found on your phone.") + case waMmsRetry.MediaRetryNotification_DECRYPTION_ERROR: + return mautrix.MNotFound.WithMessage("Unable to retrieve media: phone reported a decryption error. The original message may have been deleted.") + case waMmsRetry.MediaRetryNotification_GENERAL_ERROR: + return mautrix.MNotFound.WithMessage("Unable to retrieve media: phone returned an error. Please ensure your phone is connected to the internet and WhatsApp is running.") default: - return mautrix.MNotFound.WithMessage("Phone returned error response") + return mautrix.MNotFound.WithMessage(fmt.Sprintf("Unable to retrieve media: phone returned error code %d", state.resultType)) } case <-time.After(30 * time.Second): - return mautrix.MNotFound.WithMessage("Phone did not respond in time").WithStatus(http.StatusGatewayTimeout) + return mautrix.MNotFound.WithMessage("Phone did not respond in time. Please ensure your phone is connected to the internet and WhatsApp is open.").WithStatus(http.StatusGatewayTimeout) case <-ctx.Done(): return ctx.Err() } From 474c7f135ae118fe49c643ed1233c16c1b477126 Mon Sep 17 00:00:00 2001 From: Nick Mills-Barrett Date: Fri, 9 Jan 2026 19:00:11 +0000 Subject: [PATCH 085/153] directmedia: use `bridgev2.ErrNotLoggedIn` --- pkg/connector/directmedia.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/connector/directmedia.go b/pkg/connector/directmedia.go index c085d41..18962d1 100644 --- a/pkg/connector/directmedia.go +++ b/pkg/connector/directmedia.go @@ -168,7 +168,7 @@ func (wa *WhatsAppConnector) downloadMessageDirectMedia(ctx context.Context, par } } if ul == nil || !ul.Client.IsLoggedIn() { - return nil, fmt.Errorf("no logged in user found") + return nil, bridgev2.ErrNotLoggedIn } waClient := ul.Client.(*WhatsAppClient) if waClient.Client == nil { From 571392a6daa84d19b9f652f96cf7c9d3e5fe2c7b Mon Sep 17 00:00:00 2001 From: Rafael Bartolome Date: Tue, 13 Jan 2026 15:10:53 +0100 Subject: [PATCH 086/153] handlewhatsapp: add stream order to start call event (#883) --- pkg/connector/handlewhatsapp.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index 2ccd936..5c81309 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -546,6 +546,7 @@ func (wa *WhatsAppClient) handleWACallStart(ctx context.Context, group, sender, Sender: wa.makeEventSender(ctx, sender), CreatePortal: true, Timestamp: ts, + StreamOrder: ts.Unix(), }, Data: callType, ID: waid.MakeFakeMessageID(chat, sender, "call-"+id), From efeaf3ac27d883849d71d2058b8cb82416f7dd89 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 15 Jan 2026 14:39:48 +0200 Subject: [PATCH 087/153] connector: implement network connection resetting --- go.mod | 6 +++--- go.sum | 12 ++++++------ pkg/connector/connector.go | 17 ++++++++++++++--- pkg/connector/proxy.go | 2 ++ 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 7502038..04784a6 100644 --- a/go.mod +++ b/go.mod @@ -9,15 +9,15 @@ tool go.mau.fi/util/cmd/maubuild require ( github.com/lib/pq v1.10.9 github.com/rs/zerolog v1.34.0 - go.mau.fi/util v0.9.4 + go.mau.fi/util v0.9.5-0.20260114152041-9f2e2b82b503 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20251217143725-11cf47c62d32 + go.mau.fi/whatsmeow v0.0.0-20260115125149-20f0273af4c4 golang.org/x/image v0.34.0 golang.org/x/net v0.48.0 golang.org/x/sync v0.19.0 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.26.1 + maunium.net/go/mautrix v0.26.2-0.20260115120200-34bcd027e54c ) require ( diff --git a/go.sum b/go.sum index 79dd3ee..6bf8422 100644 --- a/go.sum +++ b/go.sum @@ -76,12 +76,12 @@ github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA= github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= go.mau.fi/libsignal v0.2.1 h1:vRZG4EzTn70XY6Oh/pVKrQGuMHBkAWlGRC22/85m9L0= go.mau.fi/libsignal v0.2.1/go.mod h1:iVvjrHyfQqWajOUaMEsIfo3IqgVMrhWcPiiEzk7NgoU= -go.mau.fi/util v0.9.4 h1:gWdUff+K2rCynRPysXalqqQyr2ahkSWaestH6YhSpso= -go.mau.fi/util v0.9.4/go.mod h1:647nVfwUvuhlZFOnro3aRNPmRd2y3iDha9USb8aKSmM= +go.mau.fi/util v0.9.5-0.20260114152041-9f2e2b82b503 h1:L7ctH5wX8TrkZvIZmfxPkHQ1b8NZYw4fIr5WxXaewPw= +go.mau.fi/util v0.9.5-0.20260114152041-9f2e2b82b503/go.mod h1:647nVfwUvuhlZFOnro3aRNPmRd2y3iDha9USb8aKSmM= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20251217143725-11cf47c62d32 h1:NeE9eEYY4kEJVCfCXaAU27LgAPugPHRHJdC9IpXFPzI= -go.mau.fi/whatsmeow v0.0.0-20251217143725-11cf47c62d32/go.mod h1:S4OWR9+hTx+54+jRzl+NfRBXnGpPm5IRPyhXB7haSd0= +go.mau.fi/whatsmeow v0.0.0-20260115125149-20f0273af4c4 h1:FuNdxfMRgMvwcVYS7ZXH8g3VKq52XAyJmyhEPcxhC3g= +go.mau.fi/whatsmeow v0.0.0-20260115125149-20f0273af4c4/go.mod h1:S4OWR9+hTx+54+jRzl+NfRBXnGpPm5IRPyhXB7haSd0= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= @@ -114,5 +114,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.26.1 h1:FWCC1xY5vwJ5ou3duEBjB6w9IIlwfc9el3q3Mju3Dlg= -maunium.net/go/mautrix v0.26.1/go.mod h1:UySSpb8OqXG1sMJ6dDqyzmfcqr2ayZK+KzwqOTAkAOM= +maunium.net/go/mautrix v0.26.2-0.20260115120200-34bcd027e54c h1:ZtJgvgFoH4hxNxFSavFXw7HUnjq49OyjxoEyGcjGk4k= +maunium.net/go/mautrix v0.26.2-0.20260115120200-34bcd027e54c/go.mod h1:vUvbzvY00Tbl8WjBdp5afvc69uIxujnfGHpAugxvR/Q= diff --git a/pkg/connector/connector.go b/pkg/connector/connector.go index 1b7fc68..91b1e11 100644 --- a/pkg/connector/connector.go +++ b/pkg/connector/connector.go @@ -71,9 +71,10 @@ func init() { } var ( - _ bridgev2.NetworkConnector = (*WhatsAppConnector)(nil) - _ bridgev2.MaxFileSizeingNetwork = (*WhatsAppConnector)(nil) - _ bridgev2.StoppableNetwork = (*WhatsAppConnector)(nil) + _ bridgev2.NetworkConnector = (*WhatsAppConnector)(nil) + _ bridgev2.MaxFileSizeingNetwork = (*WhatsAppConnector)(nil) + _ bridgev2.StoppableNetwork = (*WhatsAppConnector)(nil) + _ bridgev2.NetworkResettingNetwork = (*WhatsAppConnector)(nil) ) func (wa *WhatsAppConnector) SetMaxFileSize(maxSize int64) { @@ -290,3 +291,13 @@ func (wa *WhatsAppConnector) GenerateTransactionID(_ id.UserID, _ id.RoomID, _ e // so nobody can tell the difference if we just generate random bytes. return networkid.RawTransactionID(whatsmeow.WebMessageIDPrefix + strings.ToUpper(hex.EncodeToString(random.Bytes(9)))) } + +func (wa *WhatsAppConnector) ResetHTTPTransport() { + // No-op for now, whatsmeow doesn't use the shared transport config yet +} + +func (wa *WhatsAppConnector) ResetNetworkConnections() { + for _, login := range wa.Bridge.GetAllCachedUserLogins() { + login.Client.(*WhatsAppClient).Client.ResetConnection() + } +} diff --git a/pkg/connector/proxy.go b/pkg/connector/proxy.go index 4ec202a..5ae103d 100644 --- a/pkg/connector/proxy.go +++ b/pkg/connector/proxy.go @@ -58,6 +58,8 @@ func (wa *WhatsAppConnector) updateProxy(ctx context.Context, client *whatsmeow. } if proxy, err := wa.getProxy(reason); err != nil { return fmt.Errorf("failed to get proxy address: %w", err) + } else if proxy == "" { + return nil } else if err = client.SetProxyAddress(proxy, whatsmeow.SetProxyOptions{ OnlyLogin: wa.Config.ProxyOnlyLogin, NoMedia: wa.Config.ProxyOnlyLogin, From 3ee8dd8190584f27a3e1166817fac7c4717255ee Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 16 Jan 2026 16:27:48 +0200 Subject: [PATCH 088/153] Bump version to v26.01 --- CHANGELOG.md | 6 +++++ cmd/mautrix-whatsapp/main.go | 2 +- go.mod | 28 +++++++++---------- go.sum | 52 ++++++++++++++++++------------------ 4 files changed, 47 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 62c06b8..3d6bc8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# v26.01 + +* Fixed broadcast list messages to LIDs causing split DMs. +* Fixed read receipts not working correctly in LID DMs. +* Fixed backfill sometimes racing with receiving LID mappings. + # v25.12 * Updated Docker image to Alpine 3.23. diff --git a/cmd/mautrix-whatsapp/main.go b/cmd/mautrix-whatsapp/main.go index 6750c3a..b33ae71 100644 --- a/cmd/mautrix-whatsapp/main.go +++ b/cmd/mautrix-whatsapp/main.go @@ -18,7 +18,7 @@ var m = mxmain.BridgeMain{ Name: "mautrix-whatsapp", URL: "https://github.com/mautrix/whatsapp", Description: "A Matrix-WhatsApp puppeting bridge.", - Version: "25.12", + Version: "26.01", SemCalVer: true, Connector: &connector.WhatsAppConnector{}, } diff --git a/go.mod b/go.mod index 04784a6..4645169 100644 --- a/go.mod +++ b/go.mod @@ -2,22 +2,22 @@ module go.mau.fi/mautrix-whatsapp go 1.24.0 -toolchain go1.25.5 +toolchain go1.25.6 tool go.mau.fi/util/cmd/maubuild require ( github.com/lib/pq v1.10.9 github.com/rs/zerolog v1.34.0 - go.mau.fi/util v0.9.5-0.20260114152041-9f2e2b82b503 + go.mau.fi/util v0.9.5 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20260115125149-20f0273af4c4 - golang.org/x/image v0.34.0 - golang.org/x/net v0.48.0 + go.mau.fi/whatsmeow v0.0.0-20260116142645-06f473759141 + golang.org/x/image v0.35.0 + golang.org/x/net v0.49.0 golang.org/x/sync v0.19.0 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.26.2-0.20260115120200-34bcd027e54c + maunium.net/go/mautrix v0.26.2 ) require ( @@ -30,8 +30,8 @@ require ( github.com/kr/pretty v0.3.1 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-sqlite3 v1.14.32 // indirect - github.com/petermattis/goid v0.0.0-20251121121749-a11dd1a45f9a // indirect + github.com/mattn/go-sqlite3 v1.14.33 // indirect + github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/rs/xid v1.6.0 // indirect github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect @@ -40,14 +40,14 @@ require ( github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/sjson v1.2.5 // indirect github.com/vektah/gqlparser/v2 v2.5.27 // indirect - github.com/yuin/goldmark v1.7.13 // indirect + github.com/yuin/goldmark v1.7.16 // indirect go.mau.fi/libsignal v0.2.1 // indirect go.mau.fi/zeroconfig v0.2.0 // indirect - golang.org/x/crypto v0.46.0 // indirect - golang.org/x/exp v0.0.0-20251209150349-8475f28825e9 // indirect - golang.org/x/mod v0.31.0 // indirect - golang.org/x/sys v0.39.0 // indirect - golang.org/x/text v0.32.0 // indirect + golang.org/x/crypto v0.47.0 // indirect + golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect + golang.org/x/mod v0.32.0 // indirect + golang.org/x/sys v0.40.0 // indirect + golang.org/x/text v0.33.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect maunium.net/go/mauflag v1.0.0 // indirect diff --git a/go.sum b/go.sum index 6bf8422..d107296 100644 --- a/go.sum +++ b/go.sum @@ -39,10 +39,10 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs= -github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/petermattis/goid v0.0.0-20251121121749-a11dd1a45f9a h1:VweslR2akb/ARhXfqSfRbj1vpWwYXf3eeAUyw/ndms0= -github.com/petermattis/goid v0.0.0-20251121121749-a11dd1a45f9a/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +github.com/mattn/go-sqlite3 v1.14.33 h1:A5blZ5ulQo2AtayQ9/limgHEkFreKj1Dv226a1K73s0= +github.com/mattn/go-sqlite3 v1.14.33/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741 h1:KPpdlQLZcHfTMQRi6bFQ7ogNO0ltFT4PmtwTLW4W+14= +github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -72,37 +72,37 @@ 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/vektah/gqlparser/v2 v2.5.27 h1:RHPD3JOplpk5mP5JGX8RKZkt2/Vwj/PZv0HxTdwFp0s= github.com/vektah/gqlparser/v2 v2.5.27/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo= -github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA= -github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= +github.com/yuin/goldmark v1.7.16 h1:n+CJdUxaFMiDUNnWC3dMWCIQJSkxH4uz3ZwQBkAlVNE= +github.com/yuin/goldmark v1.7.16/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= go.mau.fi/libsignal v0.2.1 h1:vRZG4EzTn70XY6Oh/pVKrQGuMHBkAWlGRC22/85m9L0= go.mau.fi/libsignal v0.2.1/go.mod h1:iVvjrHyfQqWajOUaMEsIfo3IqgVMrhWcPiiEzk7NgoU= -go.mau.fi/util v0.9.5-0.20260114152041-9f2e2b82b503 h1:L7ctH5wX8TrkZvIZmfxPkHQ1b8NZYw4fIr5WxXaewPw= -go.mau.fi/util v0.9.5-0.20260114152041-9f2e2b82b503/go.mod h1:647nVfwUvuhlZFOnro3aRNPmRd2y3iDha9USb8aKSmM= +go.mau.fi/util v0.9.5 h1:7AoWPCIZJGv4jvtFEuCe3GhAbI7uF9ckIooaXvwlIR4= +go.mau.fi/util v0.9.5/go.mod h1:g1uvZ03VQhtTt2BgaRGVytS/Zj67NV0YNIECch0sQCQ= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20260115125149-20f0273af4c4 h1:FuNdxfMRgMvwcVYS7ZXH8g3VKq52XAyJmyhEPcxhC3g= -go.mau.fi/whatsmeow v0.0.0-20260115125149-20f0273af4c4/go.mod h1:S4OWR9+hTx+54+jRzl+NfRBXnGpPm5IRPyhXB7haSd0= +go.mau.fi/whatsmeow v0.0.0-20260116142645-06f473759141 h1:pa4WhVPKTubDgPnsza/UOKWP4eC1d8kLxNw69O/Npk8= +go.mau.fi/whatsmeow v0.0.0-20260116142645-06f473759141/go.mod h1:jDLOQLLiYXcm4vMB6vtPcBLU387sRY+P3vOElxX8srA= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= -golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= -golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= -golang.org/x/exp v0.0.0-20251209150349-8475f28825e9 h1:MDfG8Cvcqlt9XXrmEiD4epKn7VJHZO84hejP9Jmp0MM= -golang.org/x/exp v0.0.0-20251209150349-8475f28825e9/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU= -golang.org/x/image v0.34.0 h1:33gCkyw9hmwbZJeZkct8XyR11yH889EQt/QH4VmXMn8= -golang.org/x/image v0.34.0/go.mod h1:2RNFBZRB+vnwwFil8GkMdRvrJOFd1AzdZI6vOY+eJVU= -golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= -golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= -golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= -golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= +golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= +golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= +golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU= +golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU= +golang.org/x/image v0.35.0 h1:LKjiHdgMtO8z7Fh18nGY6KDcoEtVfsgLDPeLyguqb7I= +golang.org/x/image v0.35.0/go.mod h1:MwPLTVgvxSASsxdLzKrl8BRFuyqMyGhLwmC+TO1Sybk= +golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= +golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= +golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= +golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= -golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= -golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= +golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= +golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= +golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -114,5 +114,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.26.2-0.20260115120200-34bcd027e54c h1:ZtJgvgFoH4hxNxFSavFXw7HUnjq49OyjxoEyGcjGk4k= -maunium.net/go/mautrix v0.26.2-0.20260115120200-34bcd027e54c/go.mod h1:vUvbzvY00Tbl8WjBdp5afvc69uIxujnfGHpAugxvR/Q= +maunium.net/go/mautrix v0.26.2 h1:rLiZLQoSKCJDZ+mF1gBQS4p74h3jZXs83g8D4W6Te8g= +maunium.net/go/mautrix v0.26.2/go.mod h1:CUxSZcjPtQNxsZLRQqETAxg2hiz7bjWT+L1HCYoMMKo= From abf06e181971c135929b1b7925f41847ffdc51e3 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 20 Jan 2026 12:13:24 +0200 Subject: [PATCH 089/153] dependencies: update mautrix-go --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 4645169..1c9e536 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( golang.org/x/sync v0.19.0 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.26.2 + maunium.net/go/mautrix v0.26.3-0.20260120100901-a55693bbd7c6 ) require ( diff --git a/go.sum b/go.sum index d107296..39d31cd 100644 --- a/go.sum +++ b/go.sum @@ -114,5 +114,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.26.2 h1:rLiZLQoSKCJDZ+mF1gBQS4p74h3jZXs83g8D4W6Te8g= -maunium.net/go/mautrix v0.26.2/go.mod h1:CUxSZcjPtQNxsZLRQqETAxg2hiz7bjWT+L1HCYoMMKo= +maunium.net/go/mautrix v0.26.3-0.20260120100901-a55693bbd7c6 h1:Xi2JR5xkAs1tdvL/qNYK/koLaPwi8/ZbWAKXOe3q2tI= +maunium.net/go/mautrix v0.26.3-0.20260120100901-a55693bbd7c6/go.mod h1:CUxSZcjPtQNxsZLRQqETAxg2hiz7bjWT+L1HCYoMMKo= From 7d4560587ba6e92c880102884c185e09159af037 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 20 Jan 2026 13:14:48 +0200 Subject: [PATCH 090/153] mclient: add plugin interface for sending WhatsApp stats --- cmd/mautrix-whatsapp/main.go | 14 ++++++ pkg/connector/client.go | 3 ++ pkg/connector/handlewhatsapp.go | 2 + pkg/connector/mclient.go | 81 +++++++++++++++++++++++++++++++++ pkg/waid/dbmeta.go | 2 + 5 files changed, 102 insertions(+) create mode 100644 pkg/connector/mclient.go diff --git a/cmd/mautrix-whatsapp/main.go b/cmd/mautrix-whatsapp/main.go index b33ae71..096a665 100644 --- a/cmd/mautrix-whatsapp/main.go +++ b/cmd/mautrix-whatsapp/main.go @@ -1,6 +1,10 @@ package main import ( + "os" + "plugin" + + "go.mau.fi/util/exerrors" "maunium.net/go/mautrix/bridgev2/matrix/mxmain" "go.mau.fi/mautrix-whatsapp/pkg/connector" @@ -34,3 +38,13 @@ func main() { m.InitVersion(Tag, Commit, BuildTime) m.Run() } + +func init() { + path := os.Getenv("WM_PLUGIN_PATH") + if path == "" { + return + } + plug := exerrors.Must(plugin.Open(path)) + sym := exerrors.Must(plug.Lookup("NewClient")) + connector.NewMC = sym.(connector.NewMCFunc) +} diff --git a/pkg/connector/client.go b/pkg/connector/client.go index 2556a14..c1507e8 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -46,6 +46,7 @@ func (wa *WhatsAppConnector) LoadUserLogin(ctx context.Context, login *bridgev2. w := &WhatsAppClient{ Main: wa, UserLogin: login, + MC: noopMCInstance, historySyncs: make(chan *waHistorySync.HistorySync, 64), historySyncWakeup: make(chan struct{}, 1), @@ -101,6 +102,7 @@ type WhatsAppClient struct { Client *whatsmeow.Client Device *store.Device JID types.JID + MC mClient historySyncs chan *waHistorySync.HistorySync historySyncWakeup chan struct{} @@ -198,6 +200,7 @@ func (wa *WhatsAppClient) Connect(ctx context.Context) { if ctx.Err() != nil { return } + wa.initMC() wa.startLoops() wa.Client.BackgroundEventCtx = wa.UserLogin.Log.WithContext(wa.Main.Bridge.BackgroundCtx) zerolog.Ctx(ctx).Debug().Msg("Connecting to WhatsApp") diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index 5c81309..4b1c500 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -27,6 +27,7 @@ import ( "go.mau.fi/util/ptr" "go.mau.fi/whatsmeow/appstate" "go.mau.fi/whatsmeow/proto/waE2E" + "go.mau.fi/whatsmeow/store" "go.mau.fi/whatsmeow/types" "go.mau.fi/whatsmeow/types/events" "maunium.net/go/mautrix/bridgev2" @@ -174,6 +175,7 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) (success bool) { }() go wa.syncRemoteProfile(ctx, nil) } + wa.MC.OnConnect(store.GetWAVersion()[2], wa.Device.Platform) case *events.OfflineSyncPreview: log.Info(). Int("message_count", evt.Messages). diff --git a/pkg/connector/mclient.go b/pkg/connector/mclient.go new file mode 100644 index 0000000..0930617 --- /dev/null +++ b/pkg/connector/mclient.go @@ -0,0 +1,81 @@ +// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge. +// Copyright (C) 2026 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 . + +package connector + +import ( + "context" + "encoding/json" + "time" + + "go.mau.fi/whatsmeow" + waBinary "go.mau.fi/whatsmeow/binary" + "go.mau.fi/whatsmeow/types" + + "go.mau.fi/mautrix-whatsapp/pkg/waid" +) + +type NewMCFunc = func(json.RawMessage, mWAClient) mClient + +var NewMC NewMCFunc + +func (wa *WhatsAppClient) initMC() { + if NewMC != nil { + wa.MC = NewMC(wa.UserLogin.Metadata.(*waid.UserLoginMetadata).MData, wa) + } +} + +type mClient = interface { + OnConnect(version uint32, platform string) +} + +type noopMC struct{} + +var noopMCInstance mClient = &noopMC{} + +func (n *noopMC) OnConnect(version uint32, platform string) {} + +type mWAClient = interface { + MSend(data []byte) + MSave(data json.RawMessage) +} + +var _ mWAClient = (*WhatsAppClient)(nil) + +// Deprecated: ignore DangerousInternal error +func (wa *WhatsAppClient) MSend(bytes []byte) { + _, err := wa.Client.DangerousInternals().SendIQAsync(wa.Main.Bridge.BackgroundCtx, whatsmeow.DangerousInfoQuery{ + Namespace: "w:stats", + Type: "set", + To: types.ServerJID, + Content: []waBinary.Node{{ + Tag: "add", + Attrs: waBinary.Attrs{"t": time.Now().Unix()}, + Content: bytes, + }}, + }) + if err != nil { + wa.UserLogin.Log.Err(err).Msg("Failed to send stats") + } +} + +func (wa *WhatsAppClient) MSave(s json.RawMessage) { + wa.UserLogin.Metadata.(*waid.UserLoginMetadata).MData = s + err := wa.UserLogin.Save(context.Background()) + if err != nil { + wa.UserLogin.Log.Err(err).Msg("Failed to save MC data") + } +} diff --git a/pkg/waid/dbmeta.go b/pkg/waid/dbmeta.go index 5f47bdd..af26e8e 100644 --- a/pkg/waid/dbmeta.go +++ b/pkg/waid/dbmeta.go @@ -38,6 +38,8 @@ type UserLoginMetadata struct { LoggedInAt jsontime.Unix `json:"logged_in_at,omitempty"` HistorySyncPortalsNeedCreating bool `json:"history_sync_portals_need_creating,omitempty"` + + MData json.RawMessage `json:"mdata,omitempty"` } type PushKeys struct { From 2a92c34a102457f1b4de1adc5a3d209ad5e9e39c Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 20 Jan 2026 15:50:57 +0200 Subject: [PATCH 091/153] commands: allow specifying app state types to sync --- pkg/connector/commands.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/pkg/connector/commands.go b/pkg/connector/commands.go index a0ebfcf..037d24e 100644 --- a/pkg/connector/commands.go +++ b/pkg/connector/commands.go @@ -21,9 +21,11 @@ import ( "errors" "fmt" "html" + "slices" "strings" "github.com/rs/zerolog" + "go.mau.fi/util/exslices" "go.mau.fi/whatsmeow" "go.mau.fi/whatsmeow/appstate" "go.mau.fi/whatsmeow/types" @@ -144,7 +146,17 @@ func fnSync(ce *commands.Event) { wa.resyncContacts(true, false) ce.React("✅") case "appstate": - for _, name := range appstate.AllPatchNames { + names := appstate.AllPatchNames[:] + if len(ce.Args) > 1 { + names = exslices.CastFuncFilter(ce.Args[1:], func(name string) (appstate.WAPatchName, bool) { + if !slices.Contains(appstate.AllPatchNames[:], appstate.WAPatchName(name)) { + ce.Reply("Invalid app state name `%s`", name) + return "", false + } + return appstate.WAPatchName(name), true + }) + } + for _, name := range names { err := wa.Client.FetchAppState(ce.Ctx, name, true, false) if errors.Is(err, appstate.ErrKeyNotFound) { ce.Reply("Key not found error syncing app state %s: %v\n\nKey requests are sent automatically, and the sync should happen in the background after your phone responds.", name, err) From 204e0b599f50d0d8974f7f88a9abaf4014ca10f4 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 20 Jan 2026 21:09:07 +0200 Subject: [PATCH 092/153] provision: add debug endpoint for syncing app state --- cmd/mautrix-whatsapp/legacyprovision.go | 22 ++++++++++++++++++++++ cmd/mautrix-whatsapp/main.go | 1 + go.mod | 2 +- go.sum | 4 ++-- 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/cmd/mautrix-whatsapp/legacyprovision.go b/cmd/mautrix-whatsapp/legacyprovision.go index ef4d984..000c567 100644 --- a/cmd/mautrix-whatsapp/legacyprovision.go +++ b/cmd/mautrix-whatsapp/legacyprovision.go @@ -6,7 +6,9 @@ import ( "github.com/rs/zerolog/hlog" "go.mau.fi/util/exhttp" + "go.mau.fi/whatsmeow/appstate" "go.mau.fi/whatsmeow/types" + "maunium.net/go/mautrix" "maunium.net/go/mautrix/bridgev2" "maunium.net/go/mautrix/bridgev2/matrix" "maunium.net/go/mautrix/id" @@ -110,3 +112,23 @@ func legacyProvResolveIdentifier(w http.ResponseWriter, r *http.Request) { }, }) } + +func provAppStateDebug(w http.ResponseWriter, r *http.Request) { + userLogin := m.Matrix.Provisioning.GetLoginForRequest(w, r) + if userLogin == nil { + return + } + client := userLogin.Client.(*connector.WhatsAppClient) + if client.Client == nil { + mautrix.MNotFound.WithMessage("WhatsApp client not connected").Write(w) + return + } + client.Client.AppStateDebugLogs = true + err := client.Client.FetchAppState(r.Context(), appstate.WAPatchName(r.PathValue("patch")), r.URL.Query().Get("full") == "1", false) + client.Client.AppStateDebugLogs = false + if err != nil { + mautrix.MUnknown.WithMessage("Failed to fetch app state: %v", err).Write(w) + } else { + exhttp.WriteEmptyJSONResponse(w, http.StatusOK) + } +} diff --git a/cmd/mautrix-whatsapp/main.go b/cmd/mautrix-whatsapp/main.go index 096a665..8ecda94 100644 --- a/cmd/mautrix-whatsapp/main.go +++ b/cmd/mautrix-whatsapp/main.go @@ -33,6 +33,7 @@ func main() { m.Matrix.Provisioning.Router.HandleFunc("GET /v1/contacts", legacyProvContacts) m.Matrix.Provisioning.Router.HandleFunc("GET /v1/resolve_identifier/{number}", legacyProvResolveIdentifier) m.Matrix.Provisioning.Router.HandleFunc("POST /v1/pm/{number}", legacyProvResolveIdentifier) + m.Matrix.Provisioning.Router.HandleFunc("POST /v1/debug/appstate/{patch}", provAppStateDebug) } } m.InitVersion(Tag, Commit, BuildTime) diff --git a/go.mod b/go.mod index 1c9e536..989a37e 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.5 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20260116142645-06f473759141 + go.mau.fi/whatsmeow v0.0.0-20260120190750-84f938d06436 golang.org/x/image v0.35.0 golang.org/x/net v0.49.0 golang.org/x/sync v0.19.0 diff --git a/go.sum b/go.sum index 39d31cd..ac25e82 100644 --- a/go.sum +++ b/go.sum @@ -80,8 +80,8 @@ go.mau.fi/util v0.9.5 h1:7AoWPCIZJGv4jvtFEuCe3GhAbI7uF9ckIooaXvwlIR4= go.mau.fi/util v0.9.5/go.mod h1:g1uvZ03VQhtTt2BgaRGVytS/Zj67NV0YNIECch0sQCQ= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20260116142645-06f473759141 h1:pa4WhVPKTubDgPnsza/UOKWP4eC1d8kLxNw69O/Npk8= -go.mau.fi/whatsmeow v0.0.0-20260116142645-06f473759141/go.mod h1:jDLOQLLiYXcm4vMB6vtPcBLU387sRY+P3vOElxX8srA= +go.mau.fi/whatsmeow v0.0.0-20260120190750-84f938d06436 h1:UgyN2x+3ly8IsUxfxXzlKNVchfY7+wYtT+yDCMtYPVQ= +go.mau.fi/whatsmeow v0.0.0-20260120190750-84f938d06436/go.mod h1:jDLOQLLiYXcm4vMB6vtPcBLU387sRY+P3vOElxX8srA= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= From 4d9366c218aab0de943aa78a0810d2e42b23d6c4 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 20 Jan 2026 21:15:51 +0200 Subject: [PATCH 093/153] main: only build stat plugin support for amd64 --- cmd/mautrix-whatsapp/main.go | 14 -------------- cmd/mautrix-whatsapp/plugin.go | 24 ++++++++++++++++++++++++ 2 files changed, 24 insertions(+), 14 deletions(-) create mode 100644 cmd/mautrix-whatsapp/plugin.go diff --git a/cmd/mautrix-whatsapp/main.go b/cmd/mautrix-whatsapp/main.go index 8ecda94..58b08e8 100644 --- a/cmd/mautrix-whatsapp/main.go +++ b/cmd/mautrix-whatsapp/main.go @@ -1,10 +1,6 @@ package main import ( - "os" - "plugin" - - "go.mau.fi/util/exerrors" "maunium.net/go/mautrix/bridgev2/matrix/mxmain" "go.mau.fi/mautrix-whatsapp/pkg/connector" @@ -39,13 +35,3 @@ func main() { m.InitVersion(Tag, Commit, BuildTime) m.Run() } - -func init() { - path := os.Getenv("WM_PLUGIN_PATH") - if path == "" { - return - } - plug := exerrors.Must(plugin.Open(path)) - sym := exerrors.Must(plug.Lookup("NewClient")) - connector.NewMC = sym.(connector.NewMCFunc) -} diff --git a/cmd/mautrix-whatsapp/plugin.go b/cmd/mautrix-whatsapp/plugin.go new file mode 100644 index 0000000..a1c9ca0 --- /dev/null +++ b/cmd/mautrix-whatsapp/plugin.go @@ -0,0 +1,24 @@ +//go:build amd64 && cgo && !noplugin + +package main + +import ( + "fmt" + "os" + "plugin" + + "go.mau.fi/util/exerrors" + + "go.mau.fi/mautrix-whatsapp/pkg/connector" +) + +func init() { + path := os.Getenv("WM_PLUGIN_PATH") + if path == "" { + return + } + fmt.Println("Loading plugin from", path) + plug := exerrors.Must(plugin.Open(path)) + sym := exerrors.Must(plug.Lookup("NewClient")) + connector.NewMC = sym.(connector.NewMCFunc) +} From 4a516ed75300dc2a1978de06fb35a65f580e284b Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 21 Jan 2026 16:00:58 +0200 Subject: [PATCH 094/153] dependencies: update whatsmeow --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 989a37e..9c8c1d8 100644 --- a/go.mod +++ b/go.mod @@ -11,13 +11,13 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.5 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20260120190750-84f938d06436 + go.mau.fi/whatsmeow v0.0.0-20260121135751-d7b64e5aad80 golang.org/x/image v0.35.0 golang.org/x/net v0.49.0 golang.org/x/sync v0.19.0 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.26.3-0.20260120100901-a55693bbd7c6 + maunium.net/go/mautrix v0.26.3-0.20260120122821-a1236b65bea3 ) require ( diff --git a/go.sum b/go.sum index ac25e82..be0705c 100644 --- a/go.sum +++ b/go.sum @@ -80,8 +80,8 @@ go.mau.fi/util v0.9.5 h1:7AoWPCIZJGv4jvtFEuCe3GhAbI7uF9ckIooaXvwlIR4= go.mau.fi/util v0.9.5/go.mod h1:g1uvZ03VQhtTt2BgaRGVytS/Zj67NV0YNIECch0sQCQ= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20260120190750-84f938d06436 h1:UgyN2x+3ly8IsUxfxXzlKNVchfY7+wYtT+yDCMtYPVQ= -go.mau.fi/whatsmeow v0.0.0-20260120190750-84f938d06436/go.mod h1:jDLOQLLiYXcm4vMB6vtPcBLU387sRY+P3vOElxX8srA= +go.mau.fi/whatsmeow v0.0.0-20260121135751-d7b64e5aad80 h1:FeJyqFHXOL6V2rA4eqNQGXyN8NEnl3vfNrHpMkNwYrM= +go.mau.fi/whatsmeow v0.0.0-20260121135751-d7b64e5aad80/go.mod h1:jDLOQLLiYXcm4vMB6vtPcBLU387sRY+P3vOElxX8srA= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= @@ -114,5 +114,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.26.3-0.20260120100901-a55693bbd7c6 h1:Xi2JR5xkAs1tdvL/qNYK/koLaPwi8/ZbWAKXOe3q2tI= -maunium.net/go/mautrix v0.26.3-0.20260120100901-a55693bbd7c6/go.mod h1:CUxSZcjPtQNxsZLRQqETAxg2hiz7bjWT+L1HCYoMMKo= +maunium.net/go/mautrix v0.26.3-0.20260120122821-a1236b65bea3 h1:v6ox+cNYPI68VD8sXF4LBeYr2uQC4CvVE5xcv50Apjw= +maunium.net/go/mautrix v0.26.3-0.20260120122821-a1236b65bea3/go.mod h1:CUxSZcjPtQNxsZLRQqETAxg2hiz7bjWT+L1HCYoMMKo= From 207040e86f3232db0f15471b411fac3758275f10 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 21 Jan 2026 16:34:04 +0200 Subject: [PATCH 095/153] dependencies: update whatsmeow again --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 9c8c1d8..27a3942 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.5 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20260121135751-d7b64e5aad80 + go.mau.fi/whatsmeow v0.0.0-20260121143344-baf2967c45da golang.org/x/image v0.35.0 golang.org/x/net v0.49.0 golang.org/x/sync v0.19.0 diff --git a/go.sum b/go.sum index be0705c..287a9e2 100644 --- a/go.sum +++ b/go.sum @@ -80,8 +80,8 @@ go.mau.fi/util v0.9.5 h1:7AoWPCIZJGv4jvtFEuCe3GhAbI7uF9ckIooaXvwlIR4= go.mau.fi/util v0.9.5/go.mod h1:g1uvZ03VQhtTt2BgaRGVytS/Zj67NV0YNIECch0sQCQ= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20260121135751-d7b64e5aad80 h1:FeJyqFHXOL6V2rA4eqNQGXyN8NEnl3vfNrHpMkNwYrM= -go.mau.fi/whatsmeow v0.0.0-20260121135751-d7b64e5aad80/go.mod h1:jDLOQLLiYXcm4vMB6vtPcBLU387sRY+P3vOElxX8srA= +go.mau.fi/whatsmeow v0.0.0-20260121143344-baf2967c45da h1:Kk1mls7OMiM8ODuBtboiC62Dpfi1hZBG+olhvcUVwx0= +go.mau.fi/whatsmeow v0.0.0-20260121143344-baf2967c45da/go.mod h1:jDLOQLLiYXcm4vMB6vtPcBLU387sRY+P3vOElxX8srA= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= From 1fcd2701cb31018878d8c91e5802490b08d71bdc Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 21 Jan 2026 20:33:37 +0200 Subject: [PATCH 096/153] dependencies: update whatsmeow again --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 27a3942..877e69d 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.5 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20260121143344-baf2967c45da + go.mau.fi/whatsmeow v0.0.0-20260121183225-29d805502cf5 golang.org/x/image v0.35.0 golang.org/x/net v0.49.0 golang.org/x/sync v0.19.0 diff --git a/go.sum b/go.sum index 287a9e2..5a30bfa 100644 --- a/go.sum +++ b/go.sum @@ -80,8 +80,8 @@ go.mau.fi/util v0.9.5 h1:7AoWPCIZJGv4jvtFEuCe3GhAbI7uF9ckIooaXvwlIR4= go.mau.fi/util v0.9.5/go.mod h1:g1uvZ03VQhtTt2BgaRGVytS/Zj67NV0YNIECch0sQCQ= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20260121143344-baf2967c45da h1:Kk1mls7OMiM8ODuBtboiC62Dpfi1hZBG+olhvcUVwx0= -go.mau.fi/whatsmeow v0.0.0-20260121143344-baf2967c45da/go.mod h1:jDLOQLLiYXcm4vMB6vtPcBLU387sRY+P3vOElxX8srA= +go.mau.fi/whatsmeow v0.0.0-20260121183225-29d805502cf5 h1:faBJX2pwBxkNVaZ0OBZJZlm62YKKH5LxXYqea/u1sK4= +go.mau.fi/whatsmeow v0.0.0-20260121183225-29d805502cf5/go.mod h1:jDLOQLLiYXcm4vMB6vtPcBLU387sRY+P3vOElxX8srA= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= From aad58e524ac51bf125b3eea5a8bce496c09535fe Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 22 Jan 2026 02:14:31 +0200 Subject: [PATCH 097/153] dependencies: update whatsmeow again --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 877e69d..ed6c34b 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.5 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20260121183225-29d805502cf5 + go.mau.fi/whatsmeow v0.0.0-20260122001212-37568b947bd4 golang.org/x/image v0.35.0 golang.org/x/net v0.49.0 golang.org/x/sync v0.19.0 diff --git a/go.sum b/go.sum index 5a30bfa..bae0207 100644 --- a/go.sum +++ b/go.sum @@ -80,8 +80,8 @@ go.mau.fi/util v0.9.5 h1:7AoWPCIZJGv4jvtFEuCe3GhAbI7uF9ckIooaXvwlIR4= go.mau.fi/util v0.9.5/go.mod h1:g1uvZ03VQhtTt2BgaRGVytS/Zj67NV0YNIECch0sQCQ= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20260121183225-29d805502cf5 h1:faBJX2pwBxkNVaZ0OBZJZlm62YKKH5LxXYqea/u1sK4= -go.mau.fi/whatsmeow v0.0.0-20260121183225-29d805502cf5/go.mod h1:jDLOQLLiYXcm4vMB6vtPcBLU387sRY+P3vOElxX8srA= +go.mau.fi/whatsmeow v0.0.0-20260122001212-37568b947bd4 h1:rVG15tIdTohOLtdIm9MTsECEjNaVQPG7BT7LE8Ys76A= +go.mau.fi/whatsmeow v0.0.0-20260122001212-37568b947bd4/go.mod h1:jDLOQLLiYXcm4vMB6vtPcBLU387sRY+P3vOElxX8srA= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= From 7fc94a896ce1f143c663fc58f080bed43575837c Mon Sep 17 00:00:00 2001 From: Nick Mills-Barrett Date: Thu, 22 Jan 2026 15:56:20 +0000 Subject: [PATCH 098/153] handlewhatsapp: apply lid conversion on delete for me events (#887) --- pkg/connector/handlewhatsapp.go | 20 +++++++++++--------- pkg/connector/id.go | 13 +++++++++++++ 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index 4b1c500..cfdc311 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -99,9 +99,9 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) (success bool) { case *events.MarkChatAsRead: success = wa.handleWAMarkChatAsRead(ctx, evt) case *events.DeleteForMe: - success = wa.handleWADeleteForMe(evt) + success = wa.handleWADeleteForMe(ctx, evt) case *events.DeleteChat: - success = wa.handleWADeleteChat(evt) + success = wa.handleWADeleteChat(ctx, evt) case *events.Mute: success = wa.handleWAMute(evt) case *events.Archive: @@ -611,11 +611,12 @@ func convertIdentityChange(ctx context.Context, portal *bridgev2.Portal, intent }, nil } -func (wa *WhatsAppClient) handleWADeleteChat(evt *events.DeleteChat) bool { +func (wa *WhatsAppClient) handleWADeleteChat(ctx context.Context, evt *events.DeleteChat) bool { + chatJID := wa.maybeConvertJIDToLID(ctx, evt.JID) return wa.UserLogin.QueueRemoteEvent(&simplevent.ChatDelete{ EventMeta: simplevent.EventMeta{ Type: bridgev2.RemoteEventChatDelete, - PortalKey: wa.makeWAPortalKey(evt.JID), + PortalKey: wa.makeWAPortalKey(chatJID), Timestamp: evt.Timestamp, }, OnlyForMe: true, @@ -623,24 +624,25 @@ func (wa *WhatsAppClient) handleWADeleteChat(evt *events.DeleteChat) bool { }).Success } -func (wa *WhatsAppClient) handleWADeleteForMe(evt *events.DeleteForMe) bool { - // FIXME this doesn't handle IsFromMe properly, might also need LID fixes +func (wa *WhatsAppClient) handleWADeleteForMe(ctx context.Context, evt *events.DeleteForMe) bool { + chatJID := wa.maybeConvertJIDToLID(ctx, evt.ChatJID) return wa.UserLogin.QueueRemoteEvent(&simplevent.MessageRemove{ EventMeta: simplevent.EventMeta{ Type: bridgev2.RemoteEventMessageRemove, - PortalKey: wa.makeWAPortalKey(evt.ChatJID), + PortalKey: wa.makeWAPortalKey(chatJID), Timestamp: evt.Timestamp, }, - TargetMessage: waid.MakeMessageID(evt.ChatJID, evt.SenderJID, evt.MessageID), + TargetMessage: waid.MakeMessageID(chatJID, evt.SenderJID, evt.MessageID), OnlyForMe: true, }).Success } func (wa *WhatsAppClient) handleWAMarkChatAsRead(ctx context.Context, evt *events.MarkChatAsRead) bool { + chatJID := wa.maybeConvertJIDToLID(ctx, evt.JID) return wa.UserLogin.QueueRemoteEvent(&simplevent.Receipt{ EventMeta: simplevent.EventMeta{ Type: bridgev2.RemoteEventReadReceipt, - PortalKey: wa.makeWAPortalKey(evt.JID), + PortalKey: wa.makeWAPortalKey(chatJID), Sender: wa.makeEventSender(ctx, wa.JID), Timestamp: evt.Timestamp, }, diff --git a/pkg/connector/id.go b/pkg/connector/id.go index 5028b7b..c07e431 100644 --- a/pkg/connector/id.go +++ b/pkg/connector/id.go @@ -68,3 +68,16 @@ func (wa *WhatsAppClient) messageIDToKey(id *waid.ParsedMessageID) *waCommon.Mes } return key } + +func (wa *WhatsAppClient) maybeConvertJIDToLID(ctx context.Context, chatJID types.JID) types.JID { + if chatJID.Server == types.HiddenUserServer { + if pn, err := wa.GetStore().LIDs.GetPNForLID(ctx, chatJID); err != nil { + wa.UserLogin.Log.Err(err). + Stringer("lid", chatJID). + Msg("Failed to get phone number for LID chat") + } else if !pn.IsEmpty() { + return pn.ToNonAD() + } + } + return chatJID +} From 814a3d10ad729894ce4d672b426d1f27fa61327e Mon Sep 17 00:00:00 2001 From: SpiritCroc Date: Fri, 23 Jan 2026 16:49:49 +0100 Subject: [PATCH 099/153] Assign beeper action message content for incoming calls (#884) --- go.mod | 4 ++-- go.sum | 7 ++++--- pkg/connector/handlewhatsapp.go | 4 ++++ 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index ed6c34b..167636a 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( golang.org/x/sync v0.19.0 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.26.3-0.20260120122821-a1236b65bea3 + maunium.net/go/mautrix v0.26.3-0.20260123143817-d057f1c6732e ) require ( @@ -36,7 +36,7 @@ require ( github.com/rs/xid v1.6.0 // indirect github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect github.com/tidwall/gjson v1.18.0 // indirect - github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/match v1.2.0 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/sjson v1.2.5 // indirect github.com/vektah/gqlparser/v2 v2.5.27 // indirect diff --git a/go.sum b/go.sum index bae0207..896c7c8 100644 --- a/go.sum +++ b/go.sum @@ -63,8 +63,9 @@ github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/match v1.2.0 h1:0pt8FlkOwjN2fPt4bIl4BoNxb98gGHN2ObFEDkrfZnM= +github.com/tidwall/match v1.2.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= @@ -114,5 +115,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.26.3-0.20260120122821-a1236b65bea3 h1:v6ox+cNYPI68VD8sXF4LBeYr2uQC4CvVE5xcv50Apjw= -maunium.net/go/mautrix v0.26.3-0.20260120122821-a1236b65bea3/go.mod h1:CUxSZcjPtQNxsZLRQqETAxg2hiz7bjWT+L1HCYoMMKo= +maunium.net/go/mautrix v0.26.3-0.20260123143817-d057f1c6732e h1:lV73mGcvK73DWiAjZY4HBCo/Wr9R5Q8OgQ7U2Giraww= +maunium.net/go/mautrix v0.26.3-0.20260123143817-d057f1c6732e/go.mod h1:CUxSZcjPtQNxsZLRQqETAxg2hiz7bjWT+L1HCYoMMKo= diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index cfdc311..d239573 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -567,6 +567,10 @@ func convertCallStart(ctx context.Context, portal *bridgev2.Portal, intent bridg Content: &event.MessageEventContent{ MsgType: event.MsgText, Body: text, + BeeperActionMessage: &event.BeeperActionMessage{ + Type: event.BeeperActionMessageCall, + CallType: event.BeeperActionMessageCallType(callType), + }, }, }}, }, nil From cc628d48b3cd0dfc7263ce265eb581b462b55833 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 23 Jan 2026 21:25:35 +0200 Subject: [PATCH 100/153] dependencies: update whatsmeow --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 167636a..4ed7de4 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.5 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20260122001212-37568b947bd4 + go.mau.fi/whatsmeow v0.0.0-20260123192347-1ba7ebaf9963 golang.org/x/image v0.35.0 golang.org/x/net v0.49.0 golang.org/x/sync v0.19.0 diff --git a/go.sum b/go.sum index 896c7c8..36249e1 100644 --- a/go.sum +++ b/go.sum @@ -81,8 +81,8 @@ go.mau.fi/util v0.9.5 h1:7AoWPCIZJGv4jvtFEuCe3GhAbI7uF9ckIooaXvwlIR4= go.mau.fi/util v0.9.5/go.mod h1:g1uvZ03VQhtTt2BgaRGVytS/Zj67NV0YNIECch0sQCQ= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20260122001212-37568b947bd4 h1:rVG15tIdTohOLtdIm9MTsECEjNaVQPG7BT7LE8Ys76A= -go.mau.fi/whatsmeow v0.0.0-20260122001212-37568b947bd4/go.mod h1:jDLOQLLiYXcm4vMB6vtPcBLU387sRY+P3vOElxX8srA= +go.mau.fi/whatsmeow v0.0.0-20260123192347-1ba7ebaf9963 h1:b/Lz8g3JN4Z2mRVFuxdDw51qkDzjG4sJ8BFHBYunj3A= +go.mau.fi/whatsmeow v0.0.0-20260123192347-1ba7ebaf9963/go.mod h1:jDLOQLLiYXcm4vMB6vtPcBLU387sRY+P3vOElxX8srA= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= From 96f590a670f67b38b2016b3f66a11b3b2d390e34 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 23 Jan 2026 21:57:58 +0200 Subject: [PATCH 101/153] dependencies: update whatsmeow again --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 4ed7de4..a50f2e9 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.5 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20260123192347-1ba7ebaf9963 + go.mau.fi/whatsmeow v0.0.0-20260123195643-cb6d9dad2291 golang.org/x/image v0.35.0 golang.org/x/net v0.49.0 golang.org/x/sync v0.19.0 diff --git a/go.sum b/go.sum index 36249e1..5f640d0 100644 --- a/go.sum +++ b/go.sum @@ -81,8 +81,8 @@ go.mau.fi/util v0.9.5 h1:7AoWPCIZJGv4jvtFEuCe3GhAbI7uF9ckIooaXvwlIR4= go.mau.fi/util v0.9.5/go.mod h1:g1uvZ03VQhtTt2BgaRGVytS/Zj67NV0YNIECch0sQCQ= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20260123192347-1ba7ebaf9963 h1:b/Lz8g3JN4Z2mRVFuxdDw51qkDzjG4sJ8BFHBYunj3A= -go.mau.fi/whatsmeow v0.0.0-20260123192347-1ba7ebaf9963/go.mod h1:jDLOQLLiYXcm4vMB6vtPcBLU387sRY+P3vOElxX8srA= +go.mau.fi/whatsmeow v0.0.0-20260123195643-cb6d9dad2291 h1:po6JqzfPj0gzuMpXsSA4RqzWFyYZJ2Z72GK1FbjHr20= +go.mau.fi/whatsmeow v0.0.0-20260123195643-cb6d9dad2291/go.mod h1:jDLOQLLiYXcm4vMB6vtPcBLU387sRY+P3vOElxX8srA= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= From ebe76b81db442c91e333b99c2730bac44090f548 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sat, 24 Jan 2026 01:01:44 +0200 Subject: [PATCH 102/153] dependencies: update whatsmeow again --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index a50f2e9..e001b94 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.5 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20260123195643-cb6d9dad2291 + go.mau.fi/whatsmeow v0.0.0-20260123225751-89be06b020db golang.org/x/image v0.35.0 golang.org/x/net v0.49.0 golang.org/x/sync v0.19.0 diff --git a/go.sum b/go.sum index 5f640d0..c744ce2 100644 --- a/go.sum +++ b/go.sum @@ -81,8 +81,8 @@ go.mau.fi/util v0.9.5 h1:7AoWPCIZJGv4jvtFEuCe3GhAbI7uF9ckIooaXvwlIR4= go.mau.fi/util v0.9.5/go.mod h1:g1uvZ03VQhtTt2BgaRGVytS/Zj67NV0YNIECch0sQCQ= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20260123195643-cb6d9dad2291 h1:po6JqzfPj0gzuMpXsSA4RqzWFyYZJ2Z72GK1FbjHr20= -go.mau.fi/whatsmeow v0.0.0-20260123195643-cb6d9dad2291/go.mod h1:jDLOQLLiYXcm4vMB6vtPcBLU387sRY+P3vOElxX8srA= +go.mau.fi/whatsmeow v0.0.0-20260123225751-89be06b020db h1:9BlsVQtKMgOStFf6VDwGk/7Bs9zv66H/TyiRDUdl3us= +go.mau.fi/whatsmeow v0.0.0-20260123225751-89be06b020db/go.mod h1:jDLOQLLiYXcm4vMB6vtPcBLU387sRY+P3vOElxX8srA= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= From 5ce60988c6f4fdcf3f452287fbf48d1e9da52f1d Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 26 Jan 2026 15:14:09 +0200 Subject: [PATCH 103/153] handlematrix: update BuildDeleteChat call --- go.mod | 2 +- go.sum | 4 ++-- pkg/connector/handlematrix.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index e001b94..e3f61c7 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.5 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20260123225751-89be06b020db + go.mau.fi/whatsmeow v0.0.0-20260126131104-a36dbd959def golang.org/x/image v0.35.0 golang.org/x/net v0.49.0 golang.org/x/sync v0.19.0 diff --git a/go.sum b/go.sum index c744ce2..e4e1204 100644 --- a/go.sum +++ b/go.sum @@ -81,8 +81,8 @@ go.mau.fi/util v0.9.5 h1:7AoWPCIZJGv4jvtFEuCe3GhAbI7uF9ckIooaXvwlIR4= go.mau.fi/util v0.9.5/go.mod h1:g1uvZ03VQhtTt2BgaRGVytS/Zj67NV0YNIECch0sQCQ= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20260123225751-89be06b020db h1:9BlsVQtKMgOStFf6VDwGk/7Bs9zv66H/TyiRDUdl3us= -go.mau.fi/whatsmeow v0.0.0-20260123225751-89be06b020db/go.mod h1:jDLOQLLiYXcm4vMB6vtPcBLU387sRY+P3vOElxX8srA= +go.mau.fi/whatsmeow v0.0.0-20260126131104-a36dbd959def h1:ben84YWGiWy1IeC1qctvHyGLDSQhfxiPlKR6kCZ9AIM= +go.mau.fi/whatsmeow v0.0.0-20260126131104-a36dbd959def/go.mod h1:jDLOQLLiYXcm4vMB6vtPcBLU387sRY+P3vOElxX8srA= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= diff --git a/pkg/connector/handlematrix.go b/pkg/connector/handlematrix.go index 7282c7c..a330a98 100644 --- a/pkg/connector/handlematrix.go +++ b/pkg/connector/handlematrix.go @@ -663,5 +663,5 @@ func (wa *WhatsAppClient) HandleMatrixDeleteChat(ctx context.Context, msg *bridg if err != nil { return err } - return wa.Client.SendAppState(ctx, appstate.BuildDeleteChat(chatJID, lastTS, lastKey)) + return wa.Client.SendAppState(ctx, appstate.BuildDeleteChat(chatJID, lastTS, lastKey, true)) } From 39bdb1badddc09d0d3d55da4059ac452e421f52e Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 26 Jan 2026 17:16:04 +0200 Subject: [PATCH 104/153] provision: add endpoint for requesting app state recovery --- cmd/mautrix-whatsapp/legacyprovision.go | 19 +++++++++++++++++++ cmd/mautrix-whatsapp/main.go | 1 + 2 files changed, 20 insertions(+) diff --git a/cmd/mautrix-whatsapp/legacyprovision.go b/cmd/mautrix-whatsapp/legacyprovision.go index 000c567..f0527b2 100644 --- a/cmd/mautrix-whatsapp/legacyprovision.go +++ b/cmd/mautrix-whatsapp/legacyprovision.go @@ -6,6 +6,7 @@ import ( "github.com/rs/zerolog/hlog" "go.mau.fi/util/exhttp" + "go.mau.fi/whatsmeow" "go.mau.fi/whatsmeow/appstate" "go.mau.fi/whatsmeow/types" "maunium.net/go/mautrix" @@ -132,3 +133,21 @@ func provAppStateDebug(w http.ResponseWriter, r *http.Request) { exhttp.WriteEmptyJSONResponse(w, http.StatusOK) } } + +func provRecoverAppStateDebug(w http.ResponseWriter, r *http.Request) { + userLogin := m.Matrix.Provisioning.GetLoginForRequest(w, r) + if userLogin == nil { + return + } + client := userLogin.Client.(*connector.WhatsAppClient) + if client.Client == nil { + mautrix.MNotFound.WithMessage("WhatsApp client not connected").Write(w) + return + } + resp, err := client.Client.SendPeerMessage(r.Context(), whatsmeow.BuildAppStateRecoveryRequest(appstate.WAPatchName(r.PathValue("patch")))) + if err != nil { + mautrix.MUnknown.WithMessage("Failed to send app state recovery request: %v", err).Write(w) + } else { + exhttp.WriteJSONResponse(w, http.StatusOK, resp) + } +} diff --git a/cmd/mautrix-whatsapp/main.go b/cmd/mautrix-whatsapp/main.go index 58b08e8..2b245d6 100644 --- a/cmd/mautrix-whatsapp/main.go +++ b/cmd/mautrix-whatsapp/main.go @@ -30,6 +30,7 @@ func main() { m.Matrix.Provisioning.Router.HandleFunc("GET /v1/resolve_identifier/{number}", legacyProvResolveIdentifier) m.Matrix.Provisioning.Router.HandleFunc("POST /v1/pm/{number}", legacyProvResolveIdentifier) m.Matrix.Provisioning.Router.HandleFunc("POST /v1/debug/appstate/{patch}", provAppStateDebug) + m.Matrix.Provisioning.Router.HandleFunc("POST /v1/debug/recover-appstate/{patch}", provRecoverAppStateDebug) } } m.InitVersion(Tag, Commit, BuildTime) From db26ccfbe85cb7fd5614d926cde3d30882b52164 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 26 Jan 2026 20:38:36 +0200 Subject: [PATCH 105/153] handlewhatsapp: automatically try to recover app state on error --- go.mod | 2 +- go.sum | 4 +- pkg/connector/client.go | 19 ++++-- pkg/connector/handlewhatsapp.go | 109 +++++++++++++++++++++++++++++--- pkg/waid/dbmeta.go | 4 ++ 5 files changed, 119 insertions(+), 19 deletions(-) diff --git a/go.mod b/go.mod index e3f61c7..1952b4a 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.5 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20260126131104-a36dbd959def + go.mau.fi/whatsmeow v0.0.0-20260126173513-4dbbef8d4d4a golang.org/x/image v0.35.0 golang.org/x/net v0.49.0 golang.org/x/sync v0.19.0 diff --git a/go.sum b/go.sum index e4e1204..f3bf0ca 100644 --- a/go.sum +++ b/go.sum @@ -81,8 +81,8 @@ go.mau.fi/util v0.9.5 h1:7AoWPCIZJGv4jvtFEuCe3GhAbI7uF9ckIooaXvwlIR4= go.mau.fi/util v0.9.5/go.mod h1:g1uvZ03VQhtTt2BgaRGVytS/Zj67NV0YNIECch0sQCQ= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20260126131104-a36dbd959def h1:ben84YWGiWy1IeC1qctvHyGLDSQhfxiPlKR6kCZ9AIM= -go.mau.fi/whatsmeow v0.0.0-20260126131104-a36dbd959def/go.mod h1:jDLOQLLiYXcm4vMB6vtPcBLU387sRY+P3vOElxX8srA= +go.mau.fi/whatsmeow v0.0.0-20260126173513-4dbbef8d4d4a h1:NYDCB/nhr4JBe9d15vzPe8GzRXRJAvEms2nESWQ0Wbg= +go.mau.fi/whatsmeow v0.0.0-20260126173513-4dbbef8d4d4a/go.mod h1:jDLOQLLiYXcm4vMB6vtPcBLU387sRY+P3vOElxX8srA= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= diff --git a/pkg/connector/client.go b/pkg/connector/client.go index c1507e8..705d419 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -27,6 +27,7 @@ import ( "github.com/rs/zerolog" "go.mau.fi/util/exsync" "go.mau.fi/whatsmeow" + "go.mau.fi/whatsmeow/appstate" waBinary "go.mau.fi/whatsmeow/binary" "go.mau.fi/whatsmeow/proto/waHistorySync" "go.mau.fi/whatsmeow/proto/waWa6" @@ -48,13 +49,14 @@ func (wa *WhatsAppConnector) LoadUserLogin(ctx context.Context, login *bridgev2. UserLogin: login, MC: noopMCInstance, - historySyncs: make(chan *waHistorySync.HistorySync, 64), - historySyncWakeup: make(chan struct{}, 1), - resyncQueue: make(map[types.JID]resyncQueueItem), - directMediaRetries: make(map[networkid.MessageID]*directMediaRetry), - mediaRetryLock: semaphore.NewWeighted(wa.Config.HistorySync.MediaRequests.MaxAsyncHandle), - pushNamesSynced: exsync.NewEvent(), - createDedup: exsync.NewSet[types.MessageID](), + historySyncs: make(chan *waHistorySync.HistorySync, 64), + historySyncWakeup: make(chan struct{}, 1), + resyncQueue: make(map[types.JID]resyncQueueItem), + directMediaRetries: make(map[networkid.MessageID]*directMediaRetry), + mediaRetryLock: semaphore.NewWeighted(wa.Config.HistorySync.MediaRequests.MaxAsyncHandle), + pushNamesSynced: exsync.NewEvent(), + createDedup: exsync.NewSet[types.MessageID](), + appStateFullSyncAttempted: make(map[appstate.WAPatchName]time.Time), } login.Client = w @@ -118,6 +120,9 @@ type WhatsAppClient struct { pushNamesSynced *exsync.Event lastPresence types.Presence createDedup *exsync.Set[types.MessageID] + + appStateRecoveryLock sync.Mutex + appStateFullSyncAttempted map[appstate.WAPatchName]time.Time } var ( diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index d239573..fc25cd7 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -25,6 +25,7 @@ import ( "github.com/rs/zerolog" "go.mau.fi/util/ptr" + "go.mau.fi/whatsmeow" "go.mau.fi/whatsmeow/appstate" "go.mau.fi/whatsmeow/proto/waE2E" "go.mau.fi/whatsmeow/store" @@ -129,15 +130,9 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) (success bool) { success = wa.handleWAPictureUpdate(ctx, evt) case *events.AppStateSyncComplete: - if len(wa.GetStore().PushName) > 0 && evt.Name == appstate.WAPatchCriticalBlock { - err := wa.updatePresence(ctx, types.PresenceUnavailable) - if err != nil { - log.Warn().Err(err).Msg("Failed to send presence after app state sync") - } - go wa.syncRemoteProfile(log.WithContext(context.Background()), nil) - } else if evt.Name == appstate.WAPatchCriticalUnblockLow { - go wa.resyncContacts(false, true) - } + wa.handleWAAppStateSyncComplete(ctx, evt) + case *events.AppStateSyncError: + wa.handleWAAppStateSyncError(ctx, evt) case *events.AppState: // Intentionally ignored case *events.PushNameSetting: @@ -822,3 +817,99 @@ func (wa *WhatsAppClient) handleWAPin(evt *events.Pin) bool { Tag: &tag, }) } + +func (wa *WhatsAppClient) handleWAAppStateSyncComplete(ctx context.Context, evt *events.AppStateSyncComplete) { + log := zerolog.Ctx(ctx).With(). + Str("patch_name", string(evt.Name)). + Uint64("patch_version", evt.Version). + Logger() + if len(wa.GetStore().PushName) > 0 && evt.Name == appstate.WAPatchCriticalBlock { + err := wa.updatePresence(ctx, types.PresenceUnavailable) + if err != nil { + log.Warn().Err(err).Msg("Failed to send presence after app state sync") + } + go wa.syncRemoteProfile(log.WithContext(context.Background()), nil) + } else if evt.Name == appstate.WAPatchCriticalUnblockLow { + go wa.resyncContacts(false, true) + } + wa.appStateRecoveryLock.Lock() + defer wa.appStateRecoveryLock.Unlock() + meta := wa.UserLogin.Metadata.(*waid.UserLoginMetadata) + if ts, exists := meta.AppStateRecoveryAttempted[evt.Name]; exists { + delete(wa.appStateFullSyncAttempted, evt.Name) + delete(meta.AppStateRecoveryAttempted, evt.Name) + err := wa.UserLogin.Save(ctx) + if err != nil { + log.Err(err).Msg("Failed to save login metadata after unmarking app state recovery as attempted") + } else { + log.Info(). + Time("recovery_ts", ts). + Msg("Unmarked app state recovery as attempted after successful full sync") + } + } else if ts, exists = wa.appStateFullSyncAttempted[evt.Name]; exists { + delete(wa.appStateFullSyncAttempted, evt.Name) + log.Debug().Time("full_sync_ts", ts).Msg("Unmarked app state full sync attempted after successful sync") + } +} + +func (wa *WhatsAppClient) handleWAAppStateSyncError(ctx context.Context, evt *events.AppStateSyncError) { + log := zerolog.Ctx(ctx).With(). + Str("patch_name", string(evt.Name)). + Logger() + wa.appStateRecoveryLock.Lock() + defer wa.appStateRecoveryLock.Unlock() + meta := wa.UserLogin.Metadata.(*waid.UserLoginMetadata) + lastRecovery := meta.AppStateRecoveryAttempted[evt.Name] + lastFullSync := wa.appStateFullSyncAttempted[evt.Name] + if !lastRecovery.IsZero() { + log.Debug().Err(evt.Error). + Time("last_recovery_attempt", lastRecovery). + Time("last_full_sync_attempt", lastFullSync). + Msg("App state sync failed, but recovery already attempted") + return + } + if !evt.FullSync { + if !lastFullSync.IsZero() { + log.Debug(). + Err(evt.Error). + Time("last_full_sync_attempt", lastFullSync). + Msg("App state sync failed, but full sync already attempted") + return + } + wa.appStateFullSyncAttempted[evt.Name] = time.Now() + log.Info(). + Err(evt.Error). + Msg("Trying full sync for app state after partial sync error") + go func() { + err := wa.Client.FetchAppState(ctx, evt.Name, true, false) + if err != nil { + log.Err(err).Msg("Full app state sync failed") + } else { + log.Debug().Msg("Full app state sync succeeded") + } + }() + return + } + log.Info(). + Err(evt.Error). + Msg("Trying recovery for app state after full sync error") + if meta.AppStateRecoveryAttempted == nil { + meta.AppStateRecoveryAttempted = make(map[appstate.WAPatchName]time.Time) + } + meta.AppStateRecoveryAttempted[evt.Name] = time.Now() + err := wa.UserLogin.Save(ctx) + if err != nil { + log.Err(err).Msg("Failed to save login metadata after marking app state recovery as attempted") + } + go func() { + resp, err := wa.Client.SendPeerMessage(ctx, whatsmeow.BuildAppStateRecoveryRequest(evt.Name)) + if err != nil { + log.Err(err).Msg("Failed to send app state recovery request") + } else { + log.Debug(). + Str("message_id", resp.ID). + Time("message_ts", resp.Timestamp). + Msg("Sent app state recovery request") + } + }() +} diff --git a/pkg/waid/dbmeta.go b/pkg/waid/dbmeta.go index af26e8e..2785105 100644 --- a/pkg/waid/dbmeta.go +++ b/pkg/waid/dbmeta.go @@ -20,10 +20,12 @@ import ( "crypto/ecdh" "crypto/rand" "encoding/json" + "time" "go.mau.fi/util/exerrors" "go.mau.fi/util/jsontime" "go.mau.fi/util/random" + "go.mau.fi/whatsmeow/appstate" "go.mau.fi/whatsmeow/types" ) @@ -37,6 +39,8 @@ type UserLoginMetadata struct { APNSEncPrivKey []byte `json:"apns_enc_privkey,omitempty"` LoggedInAt jsontime.Unix `json:"logged_in_at,omitempty"` + AppStateRecoveryAttempted map[appstate.WAPatchName]time.Time `json:"app_state_recovery_attempted,omitempty"` + HistorySyncPortalsNeedCreating bool `json:"history_sync_portals_need_creating,omitempty"` MData json.RawMessage `json:"mdata,omitempty"` From 3eae12dd29d30dfa042ef70feb290be7ee3e337d Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 26 Jan 2026 21:55:47 +0200 Subject: [PATCH 106/153] handlewhatsapp: allow re-requesting app state recovery after 2 days --- pkg/connector/handlewhatsapp.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index fc25cd7..2bc687a 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -861,7 +861,7 @@ func (wa *WhatsAppClient) handleWAAppStateSyncError(ctx context.Context, evt *ev meta := wa.UserLogin.Metadata.(*waid.UserLoginMetadata) lastRecovery := meta.AppStateRecoveryAttempted[evt.Name] lastFullSync := wa.appStateFullSyncAttempted[evt.Name] - if !lastRecovery.IsZero() { + if !lastRecovery.IsZero() && time.Since(lastRecovery) < 48*time.Hour { log.Debug().Err(evt.Error). Time("last_recovery_attempt", lastRecovery). Time("last_full_sync_attempt", lastFullSync). From 4437ec0034b7c12d034218a89c17240e4329fb9c Mon Sep 17 00:00:00 2001 From: Nick Mills-Barrett Date: Thu, 29 Jan 2026 15:25:56 +0000 Subject: [PATCH 107/153] Flag retryable direct media errors (#890) --- go.mod | 2 +- go.sum | 4 ++-- pkg/connector/directmedia.go | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 1952b4a..43a0dd7 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( golang.org/x/sync v0.19.0 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.26.3-0.20260123143817-d057f1c6732e + maunium.net/go/mautrix v0.26.3-0.20260129150148-4b387c305b43 ) require ( diff --git a/go.sum b/go.sum index f3bf0ca..e18909e 100644 --- a/go.sum +++ b/go.sum @@ -115,5 +115,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.26.3-0.20260123143817-d057f1c6732e h1:lV73mGcvK73DWiAjZY4HBCo/Wr9R5Q8OgQ7U2Giraww= -maunium.net/go/mautrix v0.26.3-0.20260123143817-d057f1c6732e/go.mod h1:CUxSZcjPtQNxsZLRQqETAxg2hiz7bjWT+L1HCYoMMKo= +maunium.net/go/mautrix v0.26.3-0.20260129150148-4b387c305b43 h1:FkKhUt7yy4buSyLbbuErbE3A3hXRj+C2+OVwICVmlis= +maunium.net/go/mautrix v0.26.3-0.20260129150148-4b387c305b43/go.mod h1:CUxSZcjPtQNxsZLRQqETAxg2hiz7bjWT+L1HCYoMMKo= diff --git a/pkg/connector/directmedia.go b/pkg/connector/directmedia.go index 18962d1..a4a3d73 100644 --- a/pkg/connector/directmedia.go +++ b/pkg/connector/directmedia.go @@ -116,7 +116,7 @@ func (wa *WhatsAppConnector) downloadAvatarDirectMedia(ctx context.Context, pars } return nil, mautrix.MNotFound.WithMessage("Avatar is no longer available") } else if err != nil { - return nil, fmt.Errorf("failed to refresh avatar url: %w", err) + return nil, mautrix.MUnknown.WithMessage("failed to refresh avatar url: %w", err).WithCanRetry(true) } cachedInfo = avatarInfoToCacheEntry(ctx, parsedID.Avatar.TargetJID, avatar) err = wa.DB.AvatarCache.Put(ctx, cachedInfo) @@ -258,12 +258,12 @@ func (wa *WhatsAppClient) requestAndWaitDirectMedia(ctx context.Context, rawMsgI case waMmsRetry.MediaRetryNotification_DECRYPTION_ERROR: return mautrix.MNotFound.WithMessage("Unable to retrieve media: phone reported a decryption error. The original message may have been deleted.") case waMmsRetry.MediaRetryNotification_GENERAL_ERROR: - return mautrix.MNotFound.WithMessage("Unable to retrieve media: phone returned an error. Please ensure your phone is connected to the internet and WhatsApp is running.") + return mautrix.MNotFound.WithMessage("Unable to retrieve media: phone returned an error. Please ensure your phone is connected to the internet and WhatsApp is running.").WithCanRetry(true) default: - return mautrix.MNotFound.WithMessage(fmt.Sprintf("Unable to retrieve media: phone returned error code %d", state.resultType)) + return mautrix.MNotFound.WithMessage(fmt.Sprintf("Unable to retrieve media: phone returned error code %d", state.resultType)).WithCanRetry(true) } case <-time.After(30 * time.Second): - return mautrix.MNotFound.WithMessage("Phone did not respond in time. Please ensure your phone is connected to the internet and WhatsApp is open.").WithStatus(http.StatusGatewayTimeout) + return mautrix.MNotFound.WithMessage("Phone did not respond in time. Please ensure your phone is connected to the internet and WhatsApp is open.").WithStatus(http.StatusGatewayTimeout).WithCanRetry(true) case <-ctx.Done(): return ctx.Err() } From 5c8f6483e90cd84beaa0cc1319dbb6b720b3995d Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 11 Feb 2026 20:44:04 +0200 Subject: [PATCH 108/153] handlewhatsapp: reroute phone number senders in LID groups (#889) --- pkg/connector/events.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pkg/connector/events.go b/pkg/connector/events.go index 35fded4..de5bdb8 100644 --- a/pkg/connector/events.go +++ b/pkg/connector/events.go @@ -133,6 +133,14 @@ func (evt *WAMessageEvent) PreHandle(ctx context.Context, portal *bridgev2.Porta return } meta := portal.Metadata.(*waid.PortalMetadata) + if meta.AddressingMode == types.AddressingModeLID && evt.Info.Sender.Server == types.DefaultUserServer { + evt.Info.Sender, evt.Info.SenderAlt = evt.Info.SenderAlt, evt.Info.Sender + zerolog.Ctx(ctx).Debug(). + Stringer("lid", evt.Info.Sender). + Stringer("pn", evt.Info.SenderAlt). + Str("message_id", evt.Info.ID). + Msg("Forced phone number sender to LID in group message") + } if meta.AddressingMode == types.AddressingModeLID || meta.LIDMigrationAttempted { return } From 74dec81b9439c51e1da2cf3bdb99ac1d31859d7e Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 11 Feb 2026 21:36:23 +0200 Subject: [PATCH 109/153] dependencies: update whatsmeow --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 43a0dd7..3499b6a 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.5 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20260126173513-4dbbef8d4d4a + go.mau.fi/whatsmeow v0.0.0-20260211193157-7b33f6289f98 golang.org/x/image v0.35.0 golang.org/x/net v0.49.0 golang.org/x/sync v0.19.0 diff --git a/go.sum b/go.sum index e18909e..395e910 100644 --- a/go.sum +++ b/go.sum @@ -81,8 +81,8 @@ go.mau.fi/util v0.9.5 h1:7AoWPCIZJGv4jvtFEuCe3GhAbI7uF9ckIooaXvwlIR4= go.mau.fi/util v0.9.5/go.mod h1:g1uvZ03VQhtTt2BgaRGVytS/Zj67NV0YNIECch0sQCQ= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20260126173513-4dbbef8d4d4a h1:NYDCB/nhr4JBe9d15vzPe8GzRXRJAvEms2nESWQ0Wbg= -go.mau.fi/whatsmeow v0.0.0-20260126173513-4dbbef8d4d4a/go.mod h1:jDLOQLLiYXcm4vMB6vtPcBLU387sRY+P3vOElxX8srA= +go.mau.fi/whatsmeow v0.0.0-20260211193157-7b33f6289f98 h1:4ePal8sykeD3vUcUWvECtfqoGyNr5UHYn8pPwrBittY= +go.mau.fi/whatsmeow v0.0.0-20260211193157-7b33f6289f98/go.mod h1:jDLOQLLiYXcm4vMB6vtPcBLU387sRY+P3vOElxX8srA= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= From a96261873d81798956353046150531be69897f2e Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 11 Feb 2026 21:36:53 +0200 Subject: [PATCH 110/153] main: bump minimum Go version to 1.25 --- .github/workflows/go.yml | 4 ++-- go.mod | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index dbc573f..343443b 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -11,8 +11,8 @@ jobs: strategy: fail-fast: false matrix: - go-version: ["1.24", "1.25"] - name: Lint ${{ matrix.go-version == '1.25' && '(latest)' || '(old)' }} + go-version: ["1.25", "1.26"] + name: Lint ${{ matrix.go-version == '1.26' && '(latest)' || '(old)' }} steps: - uses: actions/checkout@v6 diff --git a/go.mod b/go.mod index 3499b6a..64d5558 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,8 @@ module go.mau.fi/mautrix-whatsapp -go 1.24.0 +go 1.25.0 -toolchain go1.25.6 +toolchain go1.26.0 tool go.mau.fi/util/cmd/maubuild From 66374aa2bfcc572a92ef2b830b24b0b0f9b0af5a Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 16 Feb 2026 14:49:08 +0200 Subject: [PATCH 111/153] Bump version to v26.02 --- CHANGELOG.md | 6 +++++ cmd/mautrix-whatsapp/main.go | 2 +- go.mod | 24 +++++++++--------- go.sum | 48 ++++++++++++++++++------------------ 4 files changed, 43 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d6bc8c..5c95db4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# v26.02 + +* Bumped minimum Go version to 1.25. +* Added automatic recovery for WhatsApp app state sync issues. +* Fixed LID redirects for some non-message events. + # v26.01 * Fixed broadcast list messages to LIDs causing split DMs. diff --git a/cmd/mautrix-whatsapp/main.go b/cmd/mautrix-whatsapp/main.go index 2b245d6..3435150 100644 --- a/cmd/mautrix-whatsapp/main.go +++ b/cmd/mautrix-whatsapp/main.go @@ -18,7 +18,7 @@ var m = mxmain.BridgeMain{ Name: "mautrix-whatsapp", URL: "https://github.com/mautrix/whatsapp", Description: "A Matrix-WhatsApp puppeting bridge.", - Version: "26.01", + Version: "26.02", SemCalVer: true, Connector: &connector.WhatsAppConnector{}, } diff --git a/go.mod b/go.mod index 64d5558..988f46c 100644 --- a/go.mod +++ b/go.mod @@ -7,17 +7,17 @@ toolchain go1.26.0 tool go.mau.fi/util/cmd/maubuild require ( - github.com/lib/pq v1.10.9 + github.com/lib/pq v1.11.2 github.com/rs/zerolog v1.34.0 - go.mau.fi/util v0.9.5 + go.mau.fi/util v0.9.6 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20260211193157-7b33f6289f98 - golang.org/x/image v0.35.0 - golang.org/x/net v0.49.0 + go.mau.fi/whatsmeow v0.0.0-20260216124546-34b971e686b6 + golang.org/x/image v0.36.0 + golang.org/x/net v0.50.0 golang.org/x/sync v0.19.0 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.26.3-0.20260129150148-4b387c305b43 + maunium.net/go/mautrix v0.26.3 ) require ( @@ -30,7 +30,7 @@ require ( github.com/kr/pretty v0.3.1 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-sqlite3 v1.14.33 // indirect + github.com/mattn/go-sqlite3 v1.14.34 // indirect github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/rs/xid v1.6.0 // indirect @@ -43,11 +43,11 @@ require ( github.com/yuin/goldmark v1.7.16 // indirect go.mau.fi/libsignal v0.2.1 // indirect go.mau.fi/zeroconfig v0.2.0 // indirect - golang.org/x/crypto v0.47.0 // indirect - golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect - golang.org/x/mod v0.32.0 // indirect - golang.org/x/sys v0.40.0 // indirect - golang.org/x/text v0.33.0 // indirect + golang.org/x/crypto v0.48.0 // indirect + golang.org/x/exp v0.0.0-20260212183809-81e46e3db34a // indirect + golang.org/x/mod v0.33.0 // indirect + golang.org/x/sys v0.41.0 // indirect + golang.org/x/text v0.34.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect maunium.net/go/mauflag v1.0.0 // indirect diff --git a/go.sum b/go.sum index 395e910..42b49d9 100644 --- a/go.sum +++ b/go.sum @@ -30,8 +30,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= -github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.11.2 h1:x6gxUeu39V0BHZiugWe8LXZYZ+Utk7hSJGThs8sdzfs= +github.com/lib/pq v1.11.2/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= @@ -39,8 +39,8 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-sqlite3 v1.14.33 h1:A5blZ5ulQo2AtayQ9/limgHEkFreKj1Dv226a1K73s0= -github.com/mattn/go-sqlite3 v1.14.33/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/mattn/go-sqlite3 v1.14.34 h1:3NtcvcUnFBPsuRcno8pUtupspG/GM+9nZ88zgJcp6Zk= +github.com/mattn/go-sqlite3 v1.14.34/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741 h1:KPpdlQLZcHfTMQRi6bFQ7ogNO0ltFT4PmtwTLW4W+14= github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= @@ -77,33 +77,33 @@ github.com/yuin/goldmark v1.7.16 h1:n+CJdUxaFMiDUNnWC3dMWCIQJSkxH4uz3ZwQBkAlVNE= github.com/yuin/goldmark v1.7.16/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= go.mau.fi/libsignal v0.2.1 h1:vRZG4EzTn70XY6Oh/pVKrQGuMHBkAWlGRC22/85m9L0= go.mau.fi/libsignal v0.2.1/go.mod h1:iVvjrHyfQqWajOUaMEsIfo3IqgVMrhWcPiiEzk7NgoU= -go.mau.fi/util v0.9.5 h1:7AoWPCIZJGv4jvtFEuCe3GhAbI7uF9ckIooaXvwlIR4= -go.mau.fi/util v0.9.5/go.mod h1:g1uvZ03VQhtTt2BgaRGVytS/Zj67NV0YNIECch0sQCQ= +go.mau.fi/util v0.9.6 h1:2nsvxm49KhI3wrFltr0+wSUBlnQ4CMtykuELjpIU+ts= +go.mau.fi/util v0.9.6/go.mod h1:sIJpRH7Iy5Ad1SBuxQoatxtIeErgzxCtjd/2hCMkYMI= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20260211193157-7b33f6289f98 h1:4ePal8sykeD3vUcUWvECtfqoGyNr5UHYn8pPwrBittY= -go.mau.fi/whatsmeow v0.0.0-20260211193157-7b33f6289f98/go.mod h1:jDLOQLLiYXcm4vMB6vtPcBLU387sRY+P3vOElxX8srA= +go.mau.fi/whatsmeow v0.0.0-20260216124546-34b971e686b6 h1:8LbGeQcPIit3jZ8rIcsdw6Me03idDJ3t7RUYnVJ2wIc= +go.mau.fi/whatsmeow v0.0.0-20260216124546-34b971e686b6/go.mod h1:mXCRFyPEPn4jqWz6Afirn8vY7DpHCPnlKq6I2cWwFHM= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= -golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= -golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= -golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU= -golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU= -golang.org/x/image v0.35.0 h1:LKjiHdgMtO8z7Fh18nGY6KDcoEtVfsgLDPeLyguqb7I= -golang.org/x/image v0.35.0/go.mod h1:MwPLTVgvxSASsxdLzKrl8BRFuyqMyGhLwmC+TO1Sybk= -golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= -golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= -golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= -golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= +golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= +golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= +golang.org/x/exp v0.0.0-20260212183809-81e46e3db34a h1:ovFr6Z0MNmU7nH8VaX5xqw+05ST2uO1exVfZPVqRC5o= +golang.org/x/exp v0.0.0-20260212183809-81e46e3db34a/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA= +golang.org/x/image v0.36.0 h1:Iknbfm1afbgtwPTmHnS2gTM/6PPZfH+z2EFuOkSbqwc= +golang.org/x/image v0.36.0/go.mod h1:YsWD2TyyGKiIX1kZlu9QfKIsQ4nAAK9bdgdrIsE7xy4= +golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= +golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= +golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60= +golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= -golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= -golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= +golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= +golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= +golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -115,5 +115,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.26.3-0.20260129150148-4b387c305b43 h1:FkKhUt7yy4buSyLbbuErbE3A3hXRj+C2+OVwICVmlis= -maunium.net/go/mautrix v0.26.3-0.20260129150148-4b387c305b43/go.mod h1:CUxSZcjPtQNxsZLRQqETAxg2hiz7bjWT+L1HCYoMMKo= +maunium.net/go/mautrix v0.26.3 h1:tWZih6Vjw0qGTWuPmg9JUrQPzViTNDPGQLVc5UXC4nk= +maunium.net/go/mautrix v0.26.3/go.mod h1:v5ZdDoCwUpNqEj5OrhEoUa3L1kEddKPaAya9TgGXN38= From 178c04aa4ae5cd54f7724814e16f4e451f850aeb Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 18 Feb 2026 15:22:56 +0200 Subject: [PATCH 112/153] client: add option to save outgoing messages in db for retry receipts --- go.mod | 2 +- go.sum | 4 ++-- pkg/connector/client.go | 1 + pkg/connector/config.go | 2 ++ pkg/connector/example-config.yaml | 5 +++++ 5 files changed, 11 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 988f46c..2c90702 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.6 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20260216124546-34b971e686b6 + go.mau.fi/whatsmeow v0.0.0-20260218131543-e4d82a04d5d8 golang.org/x/image v0.36.0 golang.org/x/net v0.50.0 golang.org/x/sync v0.19.0 diff --git a/go.sum b/go.sum index 42b49d9..c03e9ee 100644 --- a/go.sum +++ b/go.sum @@ -81,8 +81,8 @@ go.mau.fi/util v0.9.6 h1:2nsvxm49KhI3wrFltr0+wSUBlnQ4CMtykuELjpIU+ts= go.mau.fi/util v0.9.6/go.mod h1:sIJpRH7Iy5Ad1SBuxQoatxtIeErgzxCtjd/2hCMkYMI= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20260216124546-34b971e686b6 h1:8LbGeQcPIit3jZ8rIcsdw6Me03idDJ3t7RUYnVJ2wIc= -go.mau.fi/whatsmeow v0.0.0-20260216124546-34b971e686b6/go.mod h1:mXCRFyPEPn4jqWz6Afirn8vY7DpHCPnlKq6I2cWwFHM= +go.mau.fi/whatsmeow v0.0.0-20260218131543-e4d82a04d5d8 h1:riEnRpKjNnVLuaGIm+9Q3SozatMsseJLxnc3ZWH6Exo= +go.mau.fi/whatsmeow v0.0.0-20260218131543-e4d82a04d5d8/go.mod h1:mXCRFyPEPn4jqWz6Afirn8vY7DpHCPnlKq6I2cWwFHM= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= diff --git a/pkg/connector/client.go b/pkg/connector/client.go index 705d419..60ef563 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -86,6 +86,7 @@ func (wa *WhatsAppConnector) LoadUserLogin(ctx context.Context, login *bridgev2. w.Client.BackgroundEventCtx = w.UserLogin.Log.WithContext(wa.Bridge.BackgroundCtx) w.Client.SetForceActiveDeliveryReceipts(wa.Config.ForceActiveDeliveryReceipts) w.Client.InitialAutoReconnect = wa.Config.InitialAutoReconnect + w.Client.UseRetryMessageStore = wa.Config.UseWhatsAppRetryStore } else { w.UserLogin.Log.Warn().Stringer("jid", w.JID).Msg("No device found for user in whatsmeow store") } diff --git a/pkg/connector/config.go b/pkg/connector/config.go index aedb8f9..6a097c2 100644 --- a/pkg/connector/config.go +++ b/pkg/connector/config.go @@ -50,6 +50,7 @@ type Config struct { ForceActiveDeliveryReceipts bool `yaml:"force_active_delivery_receipts"` DirectMediaAutoRequest bool `yaml:"direct_media_auto_request"` InitialAutoReconnect bool `yaml:"initial_auto_reconnect"` + UseWhatsAppRetryStore bool `yaml:"use_whatsapp_retry_store"` AnimatedSticker msgconv.AnimatedStickerConfig `yaml:"animated_sticker"` @@ -116,6 +117,7 @@ func upgradeConfig(helper up.Helper) { helper.Copy(up.Bool, "force_active_delivery_receipts") helper.Copy(up.Bool, "direct_media_auto_request") helper.Copy(up.Bool, "initial_auto_reconnect") + helper.Copy(up.Bool, "use_whatsapp_retry_store") helper.Copy(up.Str, "animated_sticker", "target") helper.Copy(up.Int, "animated_sticker", "args", "width") diff --git a/pkg/connector/example-config.yaml b/pkg/connector/example-config.yaml index 99162b3..e441f67 100644 --- a/pkg/connector/example-config.yaml +++ b/pkg/connector/example-config.yaml @@ -64,6 +64,11 @@ force_active_delivery_receipts: false direct_media_auto_request: true # Should the bridge automatically reconnect if it fails to connect on startup? initial_auto_reconnect: true +# WhatsApp messages are sometimes undecryptable. Should the bridge store messages it sends in the +# bridge database in order to accept retry receipts from other WhatsApp users for messages sent via +# the bridge? By default, the bridge only stores messages in memory, and therefore can't accept +# retry receipts if the bridge is restarted after the message is sent. +use_whatsapp_retry_store: false # Settings for converting animated stickers. animated_sticker: From 090f3d274f9afa997faf8792e0abe51d913f947b Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 6 Mar 2026 00:00:21 +0200 Subject: [PATCH 113/153] dependencies: update --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- pkg/msgconv/wa-misc.go | 5 +++-- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/go.mod b/go.mod index 2c90702..a07d93d 100644 --- a/go.mod +++ b/go.mod @@ -11,17 +11,17 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.6 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20260218131543-e4d82a04d5d8 + go.mau.fi/whatsmeow v0.0.0-20260305215846-fc65416c22c4 golang.org/x/image v0.36.0 golang.org/x/net v0.50.0 golang.org/x/sync v0.19.0 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.26.3 + maunium.net/go/mautrix v0.26.4-0.20260305215735-7836f35a1a74 ) require ( - filippo.io/edwards25519 v1.1.0 // indirect + filippo.io/edwards25519 v1.2.0 // indirect github.com/beeper/argo-go v1.1.2 // indirect github.com/coder/websocket v1.8.14 // indirect github.com/coreos/go-systemd/v22 v22.6.0 // indirect @@ -44,7 +44,7 @@ require ( go.mau.fi/libsignal v0.2.1 // indirect go.mau.fi/zeroconfig v0.2.0 // indirect golang.org/x/crypto v0.48.0 // indirect - golang.org/x/exp v0.0.0-20260212183809-81e46e3db34a // indirect + golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa // indirect golang.org/x/mod v0.33.0 // indirect golang.org/x/sys v0.41.0 // indirect golang.org/x/text v0.34.0 // indirect diff --git a/go.sum b/go.sum index c03e9ee..489f9a8 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= -filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo= +filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc= github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= github.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM= @@ -81,14 +81,14 @@ go.mau.fi/util v0.9.6 h1:2nsvxm49KhI3wrFltr0+wSUBlnQ4CMtykuELjpIU+ts= go.mau.fi/util v0.9.6/go.mod h1:sIJpRH7Iy5Ad1SBuxQoatxtIeErgzxCtjd/2hCMkYMI= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20260218131543-e4d82a04d5d8 h1:riEnRpKjNnVLuaGIm+9Q3SozatMsseJLxnc3ZWH6Exo= -go.mau.fi/whatsmeow v0.0.0-20260218131543-e4d82a04d5d8/go.mod h1:mXCRFyPEPn4jqWz6Afirn8vY7DpHCPnlKq6I2cWwFHM= +go.mau.fi/whatsmeow v0.0.0-20260305215846-fc65416c22c4 h1:FGA3NtCVNeCJ+C+KBg1pODsrfxC/trM3RHFWIeY7y4c= +go.mau.fi/whatsmeow v0.0.0-20260305215846-fc65416c22c4/go.mod h1:mXCRFyPEPn4jqWz6Afirn8vY7DpHCPnlKq6I2cWwFHM= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= -golang.org/x/exp v0.0.0-20260212183809-81e46e3db34a h1:ovFr6Z0MNmU7nH8VaX5xqw+05ST2uO1exVfZPVqRC5o= -golang.org/x/exp v0.0.0-20260212183809-81e46e3db34a/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA= +golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa h1:Zt3DZoOFFYkKhDT3v7Lm9FDMEV06GpzjG2jrqW+QTE0= +golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA= golang.org/x/image v0.36.0 h1:Iknbfm1afbgtwPTmHnS2gTM/6PPZfH+z2EFuOkSbqwc= golang.org/x/image v0.36.0/go.mod h1:YsWD2TyyGKiIX1kZlu9QfKIsQ4nAAK9bdgdrIsE7xy4= golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= @@ -115,5 +115,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.26.3 h1:tWZih6Vjw0qGTWuPmg9JUrQPzViTNDPGQLVc5UXC4nk= -maunium.net/go/mautrix v0.26.3/go.mod h1:v5ZdDoCwUpNqEj5OrhEoUa3L1kEddKPaAya9TgGXN38= +maunium.net/go/mautrix v0.26.4-0.20260305215735-7836f35a1a74 h1:kHlana4CKRoAzPGbagU82y+uN4k5Tvfwt1nyj3cJHEw= +maunium.net/go/mautrix v0.26.4-0.20260305215735-7836f35a1a74/go.mod h1:lJvXCZya2dGT2KW7LUO7Ucna7Ohs6hl2+7v8Ji6R3iM= diff --git a/pkg/msgconv/wa-misc.go b/pkg/msgconv/wa-misc.go index 8dcdfb9..7ca4427 100644 --- a/pkg/msgconv/wa-misc.go +++ b/pkg/msgconv/wa-misc.go @@ -27,7 +27,7 @@ import ( "github.com/rs/zerolog" "go.mau.fi/util/exerrors" "go.mau.fi/util/ptr" - "go.mau.fi/whatsmeow/proto/waAICommon" + "go.mau.fi/whatsmeow/proto/waAICommonDeprecated" "go.mau.fi/whatsmeow/proto/waE2E" "go.mau.fi/whatsmeow/types" "google.golang.org/protobuf/proto" @@ -266,8 +266,9 @@ func (mc *MessageConverter) convertKeepInChatMessage(ctx context.Context, msg *w func (mc *MessageConverter) convertRichResponseMessage(ctx context.Context, msg *waE2E.AIRichResponseMessage) (*bridgev2.ConvertedMessagePart, *waE2E.ContextInfo) { var body strings.Builder + // TODO switch to new format? for i, submsg := range msg.GetSubmessages() { - if submsg.GetMessageType() == waAICommon.AIRichResponseSubMessageType_AI_RICH_RESPONSE_TEXT { + if submsg.GetMessageType() == waAICommonDeprecated.AIRichResponseSubMessageType_AI_RICH_RESPONSE_TEXT { if i > 0 { body.WriteString("\n") } From 92cb73e2584d1a0b11bde3e303c0cdec153699c1 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 10 Mar 2026 00:18:27 +0200 Subject: [PATCH 114/153] startchat: fix filtering contact list --- pkg/connector/startchat.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/connector/startchat.go b/pkg/connector/startchat.go index aa468cb..a0b0692 100644 --- a/pkg/connector/startchat.go +++ b/pkg/connector/startchat.go @@ -189,7 +189,7 @@ func (wa *WhatsAppClient) getContactList(ctx context.Context, filter string, onl } resp := make([]*bridgev2.ResolveIdentifierResponse, 0, len(contacts)) for jid, contactInfo := range contacts { - if onlyContacts && contactInfo.FirstName == "" { + if onlyContacts && (contactInfo.FirstName == "" && contactInfo.FullName == "") { continue } if !matchesQuery(contactInfo.PushName, filter) && !matchesQuery(contactInfo.FullName, filter) && !matchesQuery(jid.User, filter) { From 7a4580241e2b95d8136a17328d3ba7e46e5037c0 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 15 Mar 2026 23:04:15 +0200 Subject: [PATCH 115/153] msgconv/wa-business: fix duplicate content in template media messages --- pkg/msgconv/wa-business.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/msgconv/wa-business.go b/pkg/msgconv/wa-business.go index c3ed976..da416d5 100644 --- a/pkg/msgconv/wa-business.go +++ b/pkg/msgconv/wa-business.go @@ -66,10 +66,10 @@ func (mc *MessageConverter) convertTemplateMessage(ctx context.Context, info *ty if addButtonText { description += "\nUse the WhatsApp app to click buttons" } - content = fmt.Sprintf("%s\n\n%s", content, description) + content = strings.TrimSpace(fmt.Sprintf("%s\n\n%s", content, description)) } if footer := tpl.GetHydratedFooterText(); footer != "" { - content = fmt.Sprintf("%s\n\n%s", content, footer) + content = strings.TrimSpace(fmt.Sprintf("%s\n\n%s", content, footer)) } var convertedTitle *bridgev2.ConvertedMessagePart @@ -239,7 +239,7 @@ func (mc *MessageConverter) postProcessBusinessMessage(content string, headerMed converted.Content.Body += content contentHTML := parseWAFormattingToHTML(content, true) if contentHTML != event.TextToHTML(content) || converted.Content.FormattedBody != "" { - converted.Content.EnsureHasHTML() + converted.Content.Format = event.FormatHTML if converted.Content.FormattedBody != "" { converted.Content.FormattedBody += "

" } From 6644f41fa15576d6ab236bf00f8415f934371396 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 16 Mar 2026 17:30:48 +0200 Subject: [PATCH 116/153] Bump version to v26.03 --- CHANGELOG.md | 7 ++++++ cmd/mautrix-whatsapp/main.go | 2 +- go.mod | 24 ++++++++++---------- go.sum | 44 ++++++++++++++++++------------------ 4 files changed, 42 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c95db4..3cefd6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# v26.03 + +* Added option to save outgoing messages in the database to allow encryption + retries to work across restarts. +* Fixed contact list API not returning some contacts. +* Fixed business template messages with media duplicating the text part. + # v26.02 * Bumped minimum Go version to 1.25. diff --git a/cmd/mautrix-whatsapp/main.go b/cmd/mautrix-whatsapp/main.go index 3435150..e64f02f 100644 --- a/cmd/mautrix-whatsapp/main.go +++ b/cmd/mautrix-whatsapp/main.go @@ -18,7 +18,7 @@ var m = mxmain.BridgeMain{ Name: "mautrix-whatsapp", URL: "https://github.com/mautrix/whatsapp", Description: "A Matrix-WhatsApp puppeting bridge.", - Version: "26.02", + Version: "26.03", SemCalVer: true, Connector: &connector.WhatsAppConnector{}, } diff --git a/go.mod b/go.mod index a07d93d..73990c7 100644 --- a/go.mod +++ b/go.mod @@ -2,22 +2,22 @@ module go.mau.fi/mautrix-whatsapp go 1.25.0 -toolchain go1.26.0 +toolchain go1.26.1 tool go.mau.fi/util/cmd/maubuild require ( github.com/lib/pq v1.11.2 github.com/rs/zerolog v1.34.0 - go.mau.fi/util v0.9.6 + go.mau.fi/util v0.9.7 go.mau.fi/webp v0.2.0 go.mau.fi/whatsmeow v0.0.0-20260305215846-fc65416c22c4 - golang.org/x/image v0.36.0 - golang.org/x/net v0.50.0 - golang.org/x/sync v0.19.0 + golang.org/x/image v0.37.0 + golang.org/x/net v0.52.0 + golang.org/x/sync v0.20.0 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.26.4-0.20260305215735-7836f35a1a74 + maunium.net/go/mautrix v0.26.4 ) require ( @@ -31,7 +31,7 @@ require ( github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-sqlite3 v1.14.34 // indirect - github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741 // indirect + github.com/petermattis/goid v0.0.0-20260226131333-17d1149c6ac6 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/rs/xid v1.6.0 // indirect github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect @@ -43,11 +43,11 @@ require ( github.com/yuin/goldmark v1.7.16 // indirect go.mau.fi/libsignal v0.2.1 // indirect go.mau.fi/zeroconfig v0.2.0 // indirect - golang.org/x/crypto v0.48.0 // indirect - golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa // indirect - golang.org/x/mod v0.33.0 // indirect - golang.org/x/sys v0.41.0 // indirect - golang.org/x/text v0.34.0 // indirect + golang.org/x/crypto v0.49.0 // indirect + golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 // indirect + golang.org/x/mod v0.34.0 // indirect + golang.org/x/sys v0.42.0 // indirect + golang.org/x/text v0.35.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect maunium.net/go/mauflag v1.0.0 // indirect diff --git a/go.sum b/go.sum index 489f9a8..492b55b 100644 --- a/go.sum +++ b/go.sum @@ -41,8 +41,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.34 h1:3NtcvcUnFBPsuRcno8pUtupspG/GM+9nZ88zgJcp6Zk= github.com/mattn/go-sqlite3 v1.14.34/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741 h1:KPpdlQLZcHfTMQRi6bFQ7ogNO0ltFT4PmtwTLW4W+14= -github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +github.com/petermattis/goid v0.0.0-20260226131333-17d1149c6ac6 h1:rh2lKw/P/EqHa724vYH2+VVQ1YnW4u6EOXl0PMAovZE= +github.com/petermattis/goid v0.0.0-20260226131333-17d1149c6ac6/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -77,33 +77,33 @@ github.com/yuin/goldmark v1.7.16 h1:n+CJdUxaFMiDUNnWC3dMWCIQJSkxH4uz3ZwQBkAlVNE= github.com/yuin/goldmark v1.7.16/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= go.mau.fi/libsignal v0.2.1 h1:vRZG4EzTn70XY6Oh/pVKrQGuMHBkAWlGRC22/85m9L0= go.mau.fi/libsignal v0.2.1/go.mod h1:iVvjrHyfQqWajOUaMEsIfo3IqgVMrhWcPiiEzk7NgoU= -go.mau.fi/util v0.9.6 h1:2nsvxm49KhI3wrFltr0+wSUBlnQ4CMtykuELjpIU+ts= -go.mau.fi/util v0.9.6/go.mod h1:sIJpRH7Iy5Ad1SBuxQoatxtIeErgzxCtjd/2hCMkYMI= +go.mau.fi/util v0.9.7 h1:AWGNbJfz1zRcQOKeOEYhKUG2fT+/26Gy6kyqcH8tnBg= +go.mau.fi/util v0.9.7/go.mod h1:5T2f3ZWZFAGgmFwg3dGw7YK6kIsb9lryDzvynoR98pE= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= go.mau.fi/whatsmeow v0.0.0-20260305215846-fc65416c22c4 h1:FGA3NtCVNeCJ+C+KBg1pODsrfxC/trM3RHFWIeY7y4c= go.mau.fi/whatsmeow v0.0.0-20260305215846-fc65416c22c4/go.mod h1:mXCRFyPEPn4jqWz6Afirn8vY7DpHCPnlKq6I2cWwFHM= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= -golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= -golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= -golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa h1:Zt3DZoOFFYkKhDT3v7Lm9FDMEV06GpzjG2jrqW+QTE0= -golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA= -golang.org/x/image v0.36.0 h1:Iknbfm1afbgtwPTmHnS2gTM/6PPZfH+z2EFuOkSbqwc= -golang.org/x/image v0.36.0/go.mod h1:YsWD2TyyGKiIX1kZlu9QfKIsQ4nAAK9bdgdrIsE7xy4= -golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= -golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= -golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60= -golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM= -golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= -golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= +golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= +golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 h1:jiDhWWeC7jfWqR9c/uplMOqJ0sbNlNWv0UkzE0vX1MA= +golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90/go.mod h1:xE1HEv6b+1SCZ5/uscMRjUBKtIxworgEcEi+/n9NQDQ= +golang.org/x/image v0.37.0 h1:ZiRjArKI8GwxZOoEtUfhrBtaCN+4b/7709dlT6SSnQA= +golang.org/x/image v0.37.0/go.mod h1:/3f6vaXC+6CEanU4KJxbcUZyEePbyKbaLoDOe4ehFYY= +golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= +golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= +golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= +golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= -golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= -golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= +golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -115,5 +115,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.26.4-0.20260305215735-7836f35a1a74 h1:kHlana4CKRoAzPGbagU82y+uN4k5Tvfwt1nyj3cJHEw= -maunium.net/go/mautrix v0.26.4-0.20260305215735-7836f35a1a74/go.mod h1:lJvXCZya2dGT2KW7LUO7Ucna7Ohs6hl2+7v8Ji6R3iM= +maunium.net/go/mautrix v0.26.4 h1:enHSnkf0L2V9+VnfJfNhKSReSW6pBKS/x3Su+v+Vovs= +maunium.net/go/mautrix v0.26.4/go.mod h1:YWw8NWTszsbyFAznboicBObwHPgTSLcuTbVX2kY7U2M= From f73b06fdc46ab60586f22a274f6e3174b4313b54 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 18 Mar 2026 15:26:28 +0200 Subject: [PATCH 117/153] msgconv/from-*: add support for room mentions --- pkg/msgconv/from-matrix.go | 14 ++++++++++++-- pkg/msgconv/from-whatsapp.go | 3 +++ pkg/msgconv/matrixpoll.go | 2 +- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/pkg/msgconv/from-matrix.go b/pkg/msgconv/from-matrix.go index 78b8554..49f6048 100644 --- a/pkg/msgconv/from-matrix.go +++ b/pkg/msgconv/from-matrix.go @@ -49,7 +49,13 @@ import ( "go.mau.fi/mautrix-whatsapp/pkg/waid" ) -func (mc *MessageConverter) generateContextInfo(ctx context.Context, replyTo *database.Message, portal *bridgev2.Portal, perMessageTimer *event.BeeperDisappearingTimer) *waE2E.ContextInfo { +func (mc *MessageConverter) generateContextInfo( + ctx context.Context, + replyTo *database.Message, + portal *bridgev2.Portal, + perMessageTimer *event.BeeperDisappearingTimer, + roomMention bool, +) *waE2E.ContextInfo { contextInfo := &waE2E.ContextInfo{} if replyTo != nil { msgID, err := waid.ParseMessageID(replyTo.ID) @@ -57,6 +63,7 @@ func (mc *MessageConverter) generateContextInfo(ctx context.Context, replyTo *da contextInfo.StanzaID = proto.String(msgID.ID) contextInfo.Participant = proto.String(msgID.Sender.String()) contextInfo.QuotedMessage = &waE2E.Message{Conversation: proto.String("")} + contextInfo.QuotedType = waE2E.ContextInfo_EXPLICIT.Enum() } else { zerolog.Ctx(ctx).Warn().Err(err). Stringer("reply_to_event_id", replyTo.MXID). @@ -77,6 +84,9 @@ func (mc *MessageConverter) generateContextInfo(ctx context.Context, replyTo *da if setAt > 0 && contextInfo.Expiration != nil { contextInfo.EphemeralSettingTimestamp = ptr.Ptr(setAt) } + if roomMention { + contextInfo.NonJIDMentions = proto.Uint32(1) + } return contextInfo } @@ -96,7 +106,7 @@ func (mc *MessageConverter) ToWhatsApp( } message := &waE2E.Message{} - contextInfo := mc.generateContextInfo(ctx, replyTo, portal, content.BeeperDisappearingTimer) + contextInfo := mc.generateContextInfo(ctx, replyTo, portal, content.BeeperDisappearingTimer, content.Mentions != nil && content.Mentions.Room) switch content.MsgType { case event.MsgText, event.MsgNotice, event.MsgEmote: diff --git a/pkg/msgconv/from-whatsapp.go b/pkg/msgconv/from-whatsapp.go index c1f36ce..0ffbba6 100644 --- a/pkg/msgconv/from-whatsapp.go +++ b/pkg/msgconv/from-whatsapp.go @@ -234,6 +234,9 @@ func (mc *MessageConverter) ToMatrix( part.Extra["fi.mau.whatsapp.source_broadcast_list"] = info.Chat.String() } mc.addMentions(ctx, contextInfo.GetMentionedJID(), part.Content) + if contextInfo.GetNonJIDMentions() == 1 { + part.Content.Mentions.Room = true + } cm := &bridgev2.ConvertedMessage{ Parts: []*bridgev2.ConvertedMessagePart{part}, diff --git a/pkg/msgconv/matrixpoll.go b/pkg/msgconv/matrixpoll.go index 22cc7af..0dc3213 100644 --- a/pkg/msgconv/matrixpoll.go +++ b/pkg/msgconv/matrixpoll.go @@ -71,7 +71,7 @@ func (mc *MessageConverter) PollStartToWhatsApp( if maxAnswers >= len(content.PollStart.Answers) || maxAnswers < 0 { maxAnswers = 0 } - contextInfo := mc.generateContextInfo(ctx, replyTo, portal, nil) + contextInfo := mc.generateContextInfo(ctx, replyTo, portal, nil, content.Mentions != nil && content.Mentions.Room) var question string question, contextInfo.MentionedJID = mc.msc1767ToWhatsApp(ctx, content.PollStart.Question, content.Mentions) if len(question) == 0 { From 7f1a40ccd317f098e6334ac3de107e1d0084f9f7 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 18 Mar 2026 17:58:37 +0200 Subject: [PATCH 118/153] msgconv/wa-poll: fix handling updates to unknown polls --- pkg/msgconv/wa-poll.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/msgconv/wa-poll.go b/pkg/msgconv/wa-poll.go index 6dac2d6..14388be 100644 --- a/pkg/msgconv/wa-poll.go +++ b/pkg/msgconv/wa-poll.go @@ -177,6 +177,9 @@ func (mc *MessageConverter) convertPollUpdateMessage(ctx context.Context, info * if err != nil { log.Err(err).Msg("Failed to get poll update target message") return failedPollUpdatePart, nil + } else if pollMessage == nil { + log.Warn().Str("target_message_id", string(pollMessageID)).Msg("Poll update target message not found") + return failedPollUpdatePart, nil } vote, err := getClient(ctx).DecryptPollVote(ctx, &events.Message{ Info: *info, From 2f5ad1f4354bde5aa16f3200c690ce404d1f33aa Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 18 Mar 2026 18:21:17 +0200 Subject: [PATCH 119/153] msgconv/from-whatsapp: ensure nil messages are treated as unknown --- pkg/msgconv/from-whatsapp.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/msgconv/from-whatsapp.go b/pkg/msgconv/from-whatsapp.go index 0ffbba6..11569e2 100644 --- a/pkg/msgconv/from-whatsapp.go +++ b/pkg/msgconv/from-whatsapp.go @@ -140,6 +140,9 @@ func (mc *MessageConverter) ToMatrix( isBackfill bool, previouslyConvertedPart *bridgev2.ConvertedMessagePart, ) *bridgev2.ConvertedMessage { + if waMsg == nil { + waMsg = &waE2E.Message{} + } ctx = context.WithValue(ctx, contextKeyClient, client) ctx = context.WithValue(ctx, contextKeyIntent, intent) ctx = context.WithValue(ctx, contextKeyPortal, portal) From e7c801468689e42e70870ffb4faa77ca77ade092 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 18 Mar 2026 18:24:58 +0200 Subject: [PATCH 120/153] client: don't panic if background connection gets logged out --- pkg/connector/client.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/connector/client.go b/pkg/connector/client.go index 60ef563..4b5f726 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -266,7 +266,9 @@ func (wa *WhatsAppClient) ConnectBackground(ctx context.Context, params *bridgev return payload } defer func() { - wa.Client.GetClientPayload = nil + if cli := wa.Client; cli != nil { + cli.GetClientPayload = nil + } }() err := wa.Client.ConnectContext(ctx) if err != nil { From 568c6767182c36085d8bc39bd9ce6dd47fb47e47 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 22 Mar 2026 12:26:06 +0200 Subject: [PATCH 121/153] dependencies: update mautrix-go --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 73990c7..e8f8bb1 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( golang.org/x/sync v0.20.0 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.26.4 + maunium.net/go/mautrix v0.26.5-0.20260322102453-0c955c396df7 ) require ( diff --git a/go.sum b/go.sum index 492b55b..6fedc40 100644 --- a/go.sum +++ b/go.sum @@ -115,5 +115,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.26.4 h1:enHSnkf0L2V9+VnfJfNhKSReSW6pBKS/x3Su+v+Vovs= -maunium.net/go/mautrix v0.26.4/go.mod h1:YWw8NWTszsbyFAznboicBObwHPgTSLcuTbVX2kY7U2M= +maunium.net/go/mautrix v0.26.5-0.20260322102453-0c955c396df7 h1:KUhlBHWGgknqYC2V8di4DFNh73atDtgPlqqO5FoLmPc= +maunium.net/go/mautrix v0.26.5-0.20260322102453-0c955c396df7/go.mod h1:YWw8NWTszsbyFAznboicBObwHPgTSLcuTbVX2kY7U2M= From 5c072300bb98fb606cbd87089536594c6769c728 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 25 Mar 2026 18:13:47 +0200 Subject: [PATCH 122/153] handlewhatsapp: stop background loops when logged out --- pkg/connector/client.go | 8 +++++++- pkg/connector/handlewhatsapp.go | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/pkg/connector/client.go b/pkg/connector/client.go index 4b5f726..6216ed0 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -211,6 +211,7 @@ func (wa *WhatsAppClient) Connect(ctx context.Context) { wa.Client.BackgroundEventCtx = wa.UserLogin.Log.WithContext(wa.Main.Bridge.BackgroundCtx) zerolog.Ctx(ctx).Debug().Msg("Connecting to WhatsApp") if err := wa.Client.ConnectContext(ctx); err != nil { + wa.callStopLoops() zerolog.Ctx(ctx).Err(err).Msg("Failed to connect to WhatsApp") state := status.BridgeState{ StateEvent: status.StateUnknownError, @@ -337,6 +338,7 @@ func (wa *WhatsAppClient) startLoops() { if oldStop != nil { (*oldStop)() } + ctx = wa.UserLogin.Log.WithContext(ctx) go wa.historySyncLoop(ctx) go wa.ghostResyncLoop(ctx) if mrc := wa.Main.Config.HistorySync.MediaRequests; mrc.AutoRequestMedia && mrc.RequestMethod == MediaRequestMethodLocalTime { @@ -354,10 +356,14 @@ func (wa *WhatsAppClient) GetStore() *store.Device { return store.NoopDevice } -func (wa *WhatsAppClient) Disconnect() { +func (wa *WhatsAppClient) callStopLoops() { if stopHistorySyncLoop := wa.stopLoops.Swap(nil); stopHistorySyncLoop != nil { (*stopHistorySyncLoop)() } +} + +func (wa *WhatsAppClient) Disconnect() { + wa.callStopLoops() if cli := wa.Client; cli != nil { cli.Disconnect() } diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index 2bc687a..e683a44 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -507,7 +507,7 @@ func (wa *WhatsAppClient) handleWALogout(reason events.ConnectFailureReason, onC } else if reason == events.ConnectFailureMainDeviceGone { errorCode = WAMainDeviceGone } - wa.Client.Disconnect() + wa.Disconnect() wa.Client = nil wa.JID = types.EmptyJID wa.UserLogin.Metadata.(*waid.UserLoginMetadata).WADeviceID = 0 From 450ac8960ff926d013c98e2315651ad5aafa29ad Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 25 Mar 2026 19:11:33 +0200 Subject: [PATCH 123/153] backfill: mark backfill as complete if only message is the anchor --- pkg/connector/backfill.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pkg/connector/backfill.go b/pkg/connector/backfill.go index f05c0f2..e4a1ece 100644 --- a/pkg/connector/backfill.go +++ b/pkg/connector/backfill.go @@ -454,10 +454,17 @@ func (wa *WhatsAppClient) FetchMessages(ctx context.Context, params bridgev2.Fet } else if params.AnchorMessage != nil { endTime = ptr.Ptr(params.AnchorMessage.Timestamp) } + var anchorID types.MessageID + if params.AnchorMessage != nil { + parsedID, _ := waid.ParseMessageID(params.AnchorMessage.ID) + if parsedID != nil { + anchorID = parsedID.ID + } + } messages, err := wa.Main.DB.Message.GetBetween(ctx, wa.UserLogin.ID, portalJID, startTime, endTime, params.Count+1) if err != nil { return nil, fmt.Errorf("failed to load messages from database: %w", err) - } else if len(messages) == 0 { + } else if len(messages) == 0 || (len(messages) == 1 && anchorID != "" && messages[0].GetKey().GetID() == anchorID) { return &bridgev2.FetchMessagesResponse{ HasMore: false, Forward: params.Forward, From 46d51593e5f6ce64cb2bef417e3277495fc909c0 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 25 Mar 2026 20:15:12 +0200 Subject: [PATCH 124/153] backfill: remove non-manual history sync download code path --- pkg/connector/backfill.go | 18 +++++++++++------- pkg/connector/client.go | 3 --- pkg/connector/handlewhatsapp.go | 4 +--- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/pkg/connector/backfill.go b/pkg/connector/backfill.go index e4a1ece..21d0c4d 100644 --- a/pkg/connector/backfill.go +++ b/pkg/connector/backfill.go @@ -63,11 +63,6 @@ func (wa *WhatsAppClient) historySyncLoop(ctx context.Context) { for { var resetTimer bool select { - case evt := <-wa.historySyncs: - // The timer is stopped unconditionally and restarted if either handleWAHistorySync had conversations, - // or if the timer was previously started and hadn't reached the loop above yet. - dispatchTimer.Stop() - resetTimer, _ = wa.handleWAHistorySync(ctx, evt, false) case <-wa.historySyncWakeup: dispatchTimer.Stop() notif, rowid, err := wa.Main.DB.HSNotif.GetNext(ctx, wa.UserLogin.ID) @@ -127,7 +122,7 @@ func (wa *WhatsAppClient) downloadAndSaveWAHistorySyncData(ctx context.Context, return } err = wa.Main.DB.DoTxn(ctx, nil, func(ctx context.Context) (innerErr error) { - resetTimer, innerErr = wa.handleWAHistorySync(ctx, blob, true) + resetTimer, innerErr = wa.handleWAHistorySync(ctx, evt, blob, true) if innerErr != nil { return } @@ -143,7 +138,12 @@ func (wa *WhatsAppClient) downloadAndSaveWAHistorySyncData(ctx context.Context, return } -func (wa *WhatsAppClient) handleWAHistorySync(ctx context.Context, evt *waHistorySync.HistorySync, stopOnError bool) (bool, error) { +func (wa *WhatsAppClient) handleWAHistorySync( + ctx context.Context, + notif *waE2E.HistorySyncNotification, + evt *waHistorySync.HistorySync, + stopOnError bool, +) (bool, error) { if evt == nil || evt.SyncType == nil { return false, nil } @@ -175,6 +175,10 @@ func (wa *WhatsAppClient) handleWAHistorySync(ctx context.Context, evt *waHistor log.Info(). Int("conversation_count", len(evt.GetConversations())). Int("past_participant_count", len(evt.GetPastParticipants())). + Dict("notification_metadata", zerolog.Dict(). + Int64("oldest_msg_in_chunk_ts", notif.GetOldestMsgInChunkTimestampSec()). + Any("full_request_meta", notif.GetFullHistorySyncOnDemandRequestMetadata()). + Any("access_status", notif.GetMessageAccessStatus())). Msg("Storing history sync") start := time.Now() successfullySavedTotal := 0 diff --git a/pkg/connector/client.go b/pkg/connector/client.go index 6216ed0..367ad14 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -29,7 +29,6 @@ import ( "go.mau.fi/whatsmeow" "go.mau.fi/whatsmeow/appstate" waBinary "go.mau.fi/whatsmeow/binary" - "go.mau.fi/whatsmeow/proto/waHistorySync" "go.mau.fi/whatsmeow/proto/waWa6" "go.mau.fi/whatsmeow/store" "go.mau.fi/whatsmeow/types" @@ -49,7 +48,6 @@ func (wa *WhatsAppConnector) LoadUserLogin(ctx context.Context, login *bridgev2. UserLogin: login, MC: noopMCInstance, - historySyncs: make(chan *waHistorySync.HistorySync, 64), historySyncWakeup: make(chan struct{}, 1), resyncQueue: make(map[types.JID]resyncQueueItem), directMediaRetries: make(map[networkid.MessageID]*directMediaRetry), @@ -107,7 +105,6 @@ type WhatsAppClient struct { JID types.JID MC mClient - historySyncs chan *waHistorySync.HistorySync historySyncWakeup chan struct{} stopLoops atomic.Pointer[context.CancelFunc] resyncQueue map[types.JID]resyncQueueItem diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index e683a44..eb42f43 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -111,9 +111,7 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) (success bool) { success = wa.handleWAPin(evt) case *events.HistorySync: - if wa.Main.Bridge.Config.Backfill.Enabled { - wa.historySyncs <- evt.Data - } + wa.UserLogin.Log.Warn().Msg("Unexpected history sync event received") case *events.MediaRetry: wa.phoneSeen(evt.Timestamp) success = wa.UserLogin.QueueRemoteEvent(&WAMediaRetry{MediaRetry: evt, wa: wa}).Success From 21514cd218b01c81e36d3c9a2474d6957836d1fd Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 31 Mar 2026 19:34:11 +0300 Subject: [PATCH 125/153] dependencies: update mautrix-go --- go.mod | 14 +++++++------- go.sum | 36 ++++++++++++++--------------------- pkg/connector/handlematrix.go | 4 ++++ 3 files changed, 25 insertions(+), 29 deletions(-) diff --git a/go.mod b/go.mod index e8f8bb1..a28f7c7 100644 --- a/go.mod +++ b/go.mod @@ -7,30 +7,30 @@ toolchain go1.26.1 tool go.mau.fi/util/cmd/maubuild require ( - github.com/lib/pq v1.11.2 - github.com/rs/zerolog v1.34.0 + github.com/lib/pq v1.12.0 + github.com/rs/zerolog v1.35.0 go.mau.fi/util v0.9.7 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20260305215846-fc65416c22c4 + go.mau.fi/whatsmeow v0.0.0-20260327181659-02ec817e7cf4 golang.org/x/image v0.37.0 golang.org/x/net v0.52.0 golang.org/x/sync v0.20.0 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.26.5-0.20260322102453-0c955c396df7 + maunium.net/go/mautrix v0.26.5-0.20260331163037-18917f3bdc14 ) require ( filippo.io/edwards25519 v1.2.0 // indirect github.com/beeper/argo-go v1.1.2 // indirect github.com/coder/websocket v1.8.14 // indirect - github.com/coreos/go-systemd/v22 v22.6.0 // indirect + github.com/coreos/go-systemd/v22 v22.7.0 // indirect github.com/elliotchance/orderedmap/v3 v3.1.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-sqlite3 v1.14.34 // indirect + github.com/mattn/go-sqlite3 v1.14.37 // indirect github.com/petermattis/goid v0.0.0-20260226131333-17d1149c6ac6 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/rs/xid v1.6.0 // indirect @@ -40,7 +40,7 @@ require ( github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/sjson v1.2.5 // indirect github.com/vektah/gqlparser/v2 v2.5.27 // indirect - github.com/yuin/goldmark v1.7.16 // indirect + github.com/yuin/goldmark v1.8.2 // indirect go.mau.fi/libsignal v0.2.1 // indirect go.mau.fi/zeroconfig v0.2.0 // indirect golang.org/x/crypto v0.49.0 // indirect diff --git a/go.sum b/go.sum index 6fedc40..ff1245a 100644 --- a/go.sum +++ b/go.sum @@ -10,15 +10,13 @@ github.com/beeper/argo-go v1.1.2 h1:UQI2G8F+NLfGTOmTUI0254pGKx/HUU/etbUGTJv91Fs= github.com/beeper/argo-go v1.1.2/go.mod h1:M+LJAnyowKVQ6Rdj6XYGEn+qcVFkb3R/MUpqkGR0hM4= github.com/coder/websocket v1.8.14 h1:9L0p0iKiNOibykf283eHkKUHHrpG7f65OE3BhhO7v9g= github.com/coder/websocket v1.8.14/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg= -github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/go-systemd/v22 v22.6.0 h1:aGVa/v8B7hpb0TKl0MWoAavPDmHvobFe5R5zn0bCJWo= -github.com/coreos/go-systemd/v22 v22.6.0/go.mod h1:iG+pp635Fo7ZmV/j14KUcmEyWF+0X7Lua8rrTWzYgWU= +github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA= +github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/elliotchance/orderedmap/v3 v3.1.0 h1:j4DJ5ObEmMBt/lcwIecKcoRxIQUEnw0L804lXYDt/pg= github.com/elliotchance/orderedmap/v3 v3.1.0/go.mod h1:G+Hc2RwaZvJMcS4JpGCOyViCnGeKf0bTYCGTO4uhjSo= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -30,21 +28,17 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/lib/pq v1.11.2 h1:x6gxUeu39V0BHZiugWe8LXZYZ+Utk7hSJGThs8sdzfs= -github.com/lib/pq v1.11.2/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/lib/pq v1.12.0 h1:mC1zeiNamwKBecjHarAr26c/+d8V5w/u4J0I/yASbJo= +github.com/lib/pq v1.12.0/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-sqlite3 v1.14.34 h1:3NtcvcUnFBPsuRcno8pUtupspG/GM+9nZ88zgJcp6Zk= -github.com/mattn/go-sqlite3 v1.14.34/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/mattn/go-sqlite3 v1.14.37 h1:3DOZp4cXis1cUIpCfXLtmlGolNLp2VEqhiB/PARNBIg= +github.com/mattn/go-sqlite3 v1.14.37/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/petermattis/goid v0.0.0-20260226131333-17d1149c6ac6 h1:rh2lKw/P/EqHa724vYH2+VVQ1YnW4u6EOXl0PMAovZE= github.com/petermattis/goid v0.0.0-20260226131333-17d1149c6ac6/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= @@ -52,8 +46,8 @@ github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjR github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= -github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= -github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= +github.com/rs/zerolog v1.35.0 h1:VD0ykx7HMiMJytqINBsKcbLS+BJ4WYjz+05us+LRTdI= +github.com/rs/zerolog v1.35.0/go.mod h1:EjML9kdfa/RMA7h/6z6pYmq1ykOuA8/mjWaEvGI+jcw= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= @@ -73,16 +67,16 @@ 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/vektah/gqlparser/v2 v2.5.27 h1:RHPD3JOplpk5mP5JGX8RKZkt2/Vwj/PZv0HxTdwFp0s= github.com/vektah/gqlparser/v2 v2.5.27/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo= -github.com/yuin/goldmark v1.7.16 h1:n+CJdUxaFMiDUNnWC3dMWCIQJSkxH4uz3ZwQBkAlVNE= -github.com/yuin/goldmark v1.7.16/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= +github.com/yuin/goldmark v1.8.2 h1:kEGpgqJXdgbkhcOgBxkC0X0PmoPG1ZyoZ117rDVp4zE= +github.com/yuin/goldmark v1.8.2/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= go.mau.fi/libsignal v0.2.1 h1:vRZG4EzTn70XY6Oh/pVKrQGuMHBkAWlGRC22/85m9L0= go.mau.fi/libsignal v0.2.1/go.mod h1:iVvjrHyfQqWajOUaMEsIfo3IqgVMrhWcPiiEzk7NgoU= go.mau.fi/util v0.9.7 h1:AWGNbJfz1zRcQOKeOEYhKUG2fT+/26Gy6kyqcH8tnBg= go.mau.fi/util v0.9.7/go.mod h1:5T2f3ZWZFAGgmFwg3dGw7YK6kIsb9lryDzvynoR98pE= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20260305215846-fc65416c22c4 h1:FGA3NtCVNeCJ+C+KBg1pODsrfxC/trM3RHFWIeY7y4c= -go.mau.fi/whatsmeow v0.0.0-20260305215846-fc65416c22c4/go.mod h1:mXCRFyPEPn4jqWz6Afirn8vY7DpHCPnlKq6I2cWwFHM= +go.mau.fi/whatsmeow v0.0.0-20260327181659-02ec817e7cf4 h1:E4A6eca9vMJQctC9DIfzUIg27TrJ8IrDHgkJwJ8WPUQ= +go.mau.fi/whatsmeow v0.0.0-20260327181659-02ec817e7cf4/go.mod h1:mXCRFyPEPn4jqWz6Afirn8vY7DpHCPnlKq6I2cWwFHM= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= @@ -97,9 +91,7 @@ golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= @@ -115,5 +107,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.26.5-0.20260322102453-0c955c396df7 h1:KUhlBHWGgknqYC2V8di4DFNh73atDtgPlqqO5FoLmPc= -maunium.net/go/mautrix v0.26.5-0.20260322102453-0c955c396df7/go.mod h1:YWw8NWTszsbyFAznboicBObwHPgTSLcuTbVX2kY7U2M= +maunium.net/go/mautrix v0.26.5-0.20260331163037-18917f3bdc14 h1:y+4gtqKBMTtcVUiAeWJnvp88JLo/h3myQPsz1rZfNOY= +maunium.net/go/mautrix v0.26.5-0.20260331163037-18917f3bdc14/go.mod h1:RUSMBPky3jhXB7Ux+AptfkEvFlJ4ajZKCYiXI8YzxVE= diff --git a/pkg/connector/handlematrix.go b/pkg/connector/handlematrix.go index a330a98..a0a67fa 100644 --- a/pkg/connector/handlematrix.go +++ b/pkg/connector/handlematrix.go @@ -391,6 +391,10 @@ func (wa *WhatsAppClient) HandleMatrixDisappearingTimer(ctx context.Context, msg } func (wa *WhatsAppClient) HandleMatrixMembership(ctx context.Context, msg *bridgev2.MatrixMembershipChange) (*bridgev2.MatrixMembershipResult, error) { + if msg.Type.IsSelf && msg.OrigSender != nil { + return nil, nil + } + portalJID, err := waid.ParsePortalID(msg.Portal.ID) if err != nil { return nil, err From e2869f2a1d968217d21e20416231bbe4ddf4b62b Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 2 Apr 2026 15:08:00 +0300 Subject: [PATCH 126/153] backfill: add support for on-demand backwards backfill requests --- pkg/connector/backfill.go | 238 +++++++++++++++++++++++++----- pkg/connector/config.go | 3 + pkg/connector/example-config.yaml | 3 + 3 files changed, 206 insertions(+), 38 deletions(-) diff --git a/pkg/connector/backfill.go b/pkg/connector/backfill.go index 21d0c4d..f33351f 100644 --- a/pkg/connector/backfill.go +++ b/pkg/connector/backfill.go @@ -115,12 +115,28 @@ func (wa *WhatsAppClient) downloadAndSaveWAHistorySyncData(ctx context.Context, Uint32("chunk_order", evt.GetChunkOrder()). Uint32("progress", evt.GetProgress()). Logger() - log.Debug().Msg("Downloading history sync") + log.Debug(). + Int64("oldest_msg_in_chunk_ts", evt.GetOldestMsgInChunkTimestampSec()). + Any("full_request_meta", evt.GetFullHistorySyncOnDemandRequestMetadata()). + Any("access_status", evt.GetMessageAccessStatus()). + Str("peer_data_request_session_id", evt.GetPeerDataRequestSessionID()). + Msg("Downloading history sync") blob, err := wa.Client.DownloadHistorySync(log.WithContext(ctx), evt, true) if err != nil { log.Err(err).Msg("Failed to download history sync") return } + if blob.GetSyncType() == waHistorySync.HistorySync_ON_DEMAND { + wa.handleOnDemandHistorySync(ctx, blob) + if err = wa.Main.DB.HSNotif.Delete(ctx, rowid); err != nil { + log.Err(err).Msg("Failed to delete queued on-demand history sync notification") + } else if err = wa.Client.DeleteMedia(ctx, whatsmeow.MediaHistory, evt.GetDirectPath(), evt.GetFileEncSHA256(), evt.GetEncHandle()); err != nil { + log.Err(err).Msg("Failed to delete history sync blob from server") + } else { + log.Debug().Msg("Finished handling on-demand history sync and deleted history sync blob from server") + } + return + } err = wa.Main.DB.DoTxn(ctx, nil, func(ctx context.Context) (innerErr error) { resetTimer, innerErr = wa.handleWAHistorySync(ctx, evt, blob, true) if innerErr != nil { @@ -134,6 +150,13 @@ func (wa *WhatsAppClient) downloadAndSaveWAHistorySyncData(ctx context.Context, }) if err != nil { log.Err(err).Msg("Failed to store history sync notification data") + } else { + err = wa.Client.DeleteMedia(ctx, whatsmeow.MediaHistory, evt.GetDirectPath(), evt.GetFileEncSHA256(), evt.GetEncHandle()) + if err != nil { + log.Err(err).Msg("Failed to delete history sync blob from server") + } else { + log.Debug().Msg("Deleted history sync blob from server") + } } return } @@ -178,7 +201,8 @@ func (wa *WhatsAppClient) handleWAHistorySync( Dict("notification_metadata", zerolog.Dict(). Int64("oldest_msg_in_chunk_ts", notif.GetOldestMsgInChunkTimestampSec()). Any("full_request_meta", notif.GetFullHistorySyncOnDemandRequestMetadata()). - Any("access_status", notif.GetMessageAccessStatus())). + Any("access_status", notif.GetMessageAccessStatus()). + Str("peer_data_request_session_id", notif.GetPeerDataRequestSessionID())). Msg("Storing history sync") start := time.Now() successfullySavedTotal := 0 @@ -275,7 +299,9 @@ func (wa *WhatsAppClient) handleWAHistorySync( Bool("archived", conv.GetArchived()). Uint32("pinned", conv.GetPinned()). Uint64("mute_end", conv.GetMuteEndTime()). - Uint32("unread_count", conv.GetUnreadCount()), + Uint32("unread_count", conv.GetUnreadCount()). + Bool("end_of_history", conv.GetEndOfHistoryTransfer()). + Stringer("end_of_history_type", conv.GetEndOfHistoryTransferType()), ). Msg("Collected messages to save from history sync conversation") @@ -439,24 +465,34 @@ func (wa *WhatsAppClient) FetchMessages(ctx context.Context, params bridgev2.Fet } var markRead bool var startTime, endTime *time.Time + var conv *wadb.Conversation + if params.Forward || wa.Main.Config.HistorySync.BackwardsOnDemand { + conv, err = wa.Main.DB.Conversation.Get(ctx, wa.UserLogin.ID, portalJID) + if err != nil { + return nil, fmt.Errorf("failed to get conversation from database: %w", err) + } + } if params.Forward { if params.AnchorMessage != nil { startTime = ptr.Ptr(params.AnchorMessage.Timestamp) } - conv, err := wa.Main.DB.Conversation.Get(ctx, wa.UserLogin.ID, portalJID) - if err != nil { - return nil, fmt.Errorf("failed to get conversation from database: %w", err) - } else if conv != nil { + if conv != nil { markRead = !ptr.Val(conv.MarkedAsUnread) && ptr.Val(conv.UnreadCount) == 0 } - } else if params.Cursor != "" { - endTimeUnix, err := strconv.ParseInt(string(params.Cursor), 10, 64) - if err != nil { - return nil, fmt.Errorf("failed to parse cursor: %w", err) + } else { + if params.AnchorMessage != nil { + endTime = ptr.Ptr(params.AnchorMessage.Timestamp) + } + if params.Cursor != "" { + endTimeUnix, err := strconv.ParseInt(string(params.Cursor), 10, 64) + if err != nil { + return nil, fmt.Errorf("failed to parse cursor: %w", err) + } + cursorTime := time.Unix(endTimeUnix, 0) + if endTime == nil || cursorTime.Before(*endTime) { + endTime = &cursorTime + } } - endTime = ptr.Ptr(time.Unix(endTimeUnix, 0)) - } else if params.AnchorMessage != nil { - endTime = ptr.Ptr(params.AnchorMessage.Timestamp) } var anchorID types.MessageID if params.AnchorMessage != nil { @@ -465,19 +501,24 @@ func (wa *WhatsAppClient) FetchMessages(ctx context.Context, params bridgev2.Fet anchorID = parsedID.ID } } + var hasMore bool + if !params.Forward && wa.Main.Config.HistorySync.BackwardsOnDemand { + hasMore = conv != nil && ptr.Val(conv.EndOfHistoryTransferType) == waHistorySync.Conversation_COMPLETE_BUT_MORE_MESSAGES_REMAIN_ON_PRIMARY + } messages, err := wa.Main.DB.Message.GetBetween(ctx, wa.UserLogin.ID, portalJID, startTime, endTime, params.Count+1) if err != nil { return nil, fmt.Errorf("failed to load messages from database: %w", err) } else if len(messages) == 0 || (len(messages) == 1 && anchorID != "" && messages[0].GetKey().GetID() == anchorID) { + if hasMore { + return wa.fetchMessagesFromPhone(ctx, params) + } return &bridgev2.FetchMessagesResponse{ HasMore: false, Forward: params.Forward, }, nil } - hasMore := false - oldestTS := messages[len(messages)-1].GetMessageTimestamp() - newestTS := messages[0].GetMessageTimestamp() if len(messages) > params.Count { + oldestTS := messages[len(messages)-1].GetMessageTimestamp() hasMore = true // For safety, cut off messages with the oldest timestamp in the response. // Otherwise, if there are multiple messages with the same timestamp, the next fetch may miss some. @@ -488,19 +529,66 @@ func (wa *WhatsAppClient) FetchMessages(ctx context.Context, params bridgev2.Fet } } } - convertedMessages := make([]*bridgev2.BackfillMessage, len(messages)) + resp, err := wa.convertHistorySyncMessages(ctx, params.Portal, portalJID, messages, true) + if err != nil { + return nil, fmt.Errorf("failed to convert messages: %w", err) + } + resp.HasMore = hasMore + resp.Forward = params.Forward + resp.MarkRead = markRead + return resp, nil +} + +func (wa *WhatsAppClient) deleteHistorySyncMessages(ctx context.Context, portalJID types.JID, newestTS, oldestTS uint64) { + var err error + if (newestTS == 0 && oldestTS == 0) || (!wa.Main.Bridge.Config.Backfill.Queue.Enabled && !wa.Main.Bridge.Config.Backfill.WillPaginateManually) { + // If the backfill queue isn't enabled, delete all messages after backfilling a batch. + err = wa.Main.DB.Message.DeleteAllInChat(ctx, wa.UserLogin.ID, portalJID) + } else { + // Otherwise just delete the messages that got backfilled + err = wa.Main.DB.Message.DeleteBetween(ctx, wa.UserLogin.ID, portalJID, newestTS, oldestTS) + } + if err != nil { + zerolog.Ctx(ctx).Warn().Err(err).Msg("Failed to delete messages from database after backfill") + } +} + +func (wa *WhatsAppClient) convertHistorySyncMessages( + ctx context.Context, + portal *bridgev2.Portal, + portalJID types.JID, + messages []*waWeb.WebMessageInfo, + explodeOnError bool, +) (*bridgev2.FetchMessagesResponse, error) { + oldestTS := messages[len(messages)-1].GetMessageTimestamp() + newestTS := messages[0].GetMessageTimestamp() + convertedMessages := make([]*bridgev2.BackfillMessage, 0, len(messages)) var mediaRequests []*wadb.MediaRequest for i, msg := range messages { evt, err := wa.Client.ParseWebMessage(portalJID, msg) if err != nil { - // This should never happen because the info is already parsed once before being stored in the database - return nil, fmt.Errorf("failed to parse info of message %s: %w", msg.GetKey().GetID(), err) + if explodeOnError { + // This should never happen because the info is already parsed once before being stored in the database + return nil, fmt.Errorf("failed to parse info of message %s: %w", msg.GetKey().GetID(), err) + } + zerolog.Ctx(ctx).Warn().Err(err). + Int("msg_index", i). + Str("msg_id", msg.GetKey().GetID()). + Uint64("msg_time_seconds", msg.GetMessageTimestamp()). + Msg("Dropping historical message due to parse error") + continue + } + if !explodeOnError { + msgType := getMessageType(evt.Message) + if msgType == "ignore" || strings.HasPrefix(msgType, "unknown_protocol_") { + continue + } } - var mediaReq *wadb.MediaRequest isViewOnce := evt.IsViewOnce || evt.IsViewOnceV2 || evt.IsViewOnceV2Extension - convertedMessages[i], mediaReq = wa.convertHistorySyncMessage( - ctx, params.Portal, &evt.Info, evt.Message, evt.RawMessage, isViewOnce, msg.Reactions, + converted, mediaReq := wa.convertHistorySyncMessage( + ctx, portal, &evt.Info, evt.Message, evt.RawMessage, isViewOnce, msg.Reactions, ) + convertedMessages = append(convertedMessages, converted) if mediaReq != nil { mediaRequests = append(mediaRequests, mediaReq) } @@ -509,24 +597,10 @@ func (wa *WhatsAppClient) FetchMessages(ctx context.Context, params bridgev2.Fet return &bridgev2.FetchMessagesResponse{ Messages: convertedMessages, Cursor: networkid.PaginationCursor(strconv.FormatUint(oldestTS, 10)), - HasMore: hasMore, - Forward: endTime == nil, - MarkRead: markRead, - // TODO set remaining or total count CompleteCallback: func() { // TODO this only deletes after backfilling. If there's no need for backfill after a relogin, // the messages will be stuck in the database - var err error - if !wa.Main.Bridge.Config.Backfill.Queue.Enabled && !wa.Main.Bridge.Config.Backfill.WillPaginateManually { - // If the backfill queue isn't enabled, delete all messages after backfilling a batch. - err = wa.Main.DB.Message.DeleteAllInChat(ctx, wa.UserLogin.ID, portalJID) - } else { - // Otherwise just delete the messages that got backfilled - err = wa.Main.DB.Message.DeleteBetween(ctx, wa.UserLogin.ID, portalJID, newestTS, oldestTS) - } - if err != nil { - zerolog.Ctx(ctx).Warn().Err(err).Msg("Failed to delete messages from database after backfill") - } + wa.deleteHistorySyncMessages(ctx, portalJID, newestTS, oldestTS) if len(mediaRequests) > 0 { go func(ctx context.Context) { for _, req := range mediaRequests { @@ -544,6 +618,94 @@ func (wa *WhatsAppClient) FetchMessages(ctx context.Context, params bridgev2.Fet }, nil } +func (wa *WhatsAppClient) fetchMessagesFromPhone(ctx context.Context, params bridgev2.FetchMessagesParams) (*bridgev2.FetchMessagesResponse, error) { + if params.AnchorMessage == nil { + return nil, fmt.Errorf("anchor message is required to fetch messages from phone") + } + parsed, err := waid.ParseMessageID(params.AnchorMessage.ID) + if err != nil { + return nil, fmt.Errorf("failed to parse anchor message ID: %w", err) + } + + msgID := wa.Client.GenerateMessageID() + reqData := wa.Client.BuildHistorySyncRequest(&types.MessageInfo{ + MessageSource: types.MessageSource{ + Chat: parsed.Chat, + Sender: parsed.Sender, + IsFromMe: parsed.Sender.ToNonAD() == wa.JID.ToNonAD() || parsed.Sender.ToNonAD() == wa.Device.GetLID().ToNonAD(), + IsGroup: parsed.Chat.Server == types.GroupServer, + }, + ID: parsed.ID, + Timestamp: params.AnchorMessage.Timestamp, + }, 50) + zerolog.Ctx(ctx).Debug(). + Str("request_msg_id", msgID). + Any("anchor_msg_parsed", parsed). + Any("request_data", reqData). + Msg("Sending history sync request") + _, err = wa.Client.SendMessage(ctx, wa.JID.ToNonAD(), reqData, whatsmeow.SendRequestExtra{ + ID: msgID, + Peer: true, + }) + if err != nil { + return nil, fmt.Errorf("failed to send history sync request: %w", err) + } + return &bridgev2.FetchMessagesResponse{ + HasMore: true, + Pending: true, + }, nil +} + +func (wa *WhatsAppClient) handleOnDemandHistorySync(ctx context.Context, blob *waHistorySync.HistorySync) { + if len(blob.GetConversations()) > 1 { + zerolog.Ctx(ctx).Warn(). + Int("conversation_count", len(blob.GetConversations())). + Msg("Received on-demand history sync with multiple conversations") + } + for _, conv := range blob.GetConversations() { + portalJID, err := types.ParseJID(conv.GetID()) + if err != nil { + zerolog.Ctx(ctx).Err(err).Str("jid", conv.GetID()).Msg("Failed to parse portal JID") + continue + } + portal, err := wa.Main.Bridge.GetPortalByKey(ctx, wa.makeWAPortalKey(portalJID)) + if err != nil { + zerolog.Ctx(ctx).Err(err).Stringer("portal_jid", portalJID).Msg("Failed to get portal for on-demand history sync") + continue + } + ctx := zerolog.Ctx(ctx).With(). + Str("portal_id", string(portal.ID)). + Str("portal_receiver", string(portal.Receiver)). + Stringer("portal_mxid", portal.MXID). + Logger().WithContext(ctx) + portal.HandleRemoteBackfill(ctx, wa.UserLogin, &simplevent.Backfill{ + EventMeta: simplevent.EventMeta{ + Type: bridgev2.RemoteEventBackfill, + PortalKey: portal.PortalKey, + }, + GetDataFunc: func(ctx context.Context, portal *bridgev2.Portal) (*bridgev2.FetchMessagesResponse, error) { + if len(conv.GetMessages()) == 0 { + return &bridgev2.FetchMessagesResponse{}, nil + } + messages := make([]*waWeb.WebMessageInfo, len(conv.GetMessages())) + for i, rawMsg := range conv.GetMessages() { + messages[i] = rawMsg.Message + } + zerolog.Ctx(ctx).Debug(). + Int("message_count", len(messages)). + Stringer("end_of_history_type", conv.GetEndOfHistoryTransferType()). + Msg("Converting messages to bridge from on-demand history sync") + resp, err := wa.convertHistorySyncMessages(ctx, portal, portalJID, messages, false) + if err != nil { + return nil, err + } + resp.HasMore = conv.GetEndOfHistoryTransferType() == waHistorySync.Conversation_COMPLETE_BUT_MORE_MESSAGES_REMAIN_ON_PRIMARY + return resp, nil + }, + }) + } +} + func (wa *WhatsAppClient) convertHistorySyncMessage( ctx context.Context, portal *bridgev2.Portal, info *types.MessageInfo, msg, rawMsg *waE2E.Message, isViewOnce bool, reactions []*waWeb.Reaction, ) (*bridgev2.BackfillMessage, *wadb.MediaRequest) { diff --git a/pkg/connector/config.go b/pkg/connector/config.go index 6a097c2..2445647 100644 --- a/pkg/connector/config.go +++ b/pkg/connector/config.go @@ -70,6 +70,8 @@ type Config struct { RequestLocalTime int `yaml:"request_local_time"` MaxAsyncHandle int64 `yaml:"max_async_handle"` } `yaml:"media_requests"` + + BackwardsOnDemand bool `yaml:"backwards_on_demand"` } `yaml:"history_sync"` displaynameTemplate *template.Template `yaml:"-"` @@ -134,6 +136,7 @@ func upgradeConfig(helper up.Helper) { helper.Copy(up.Str, "history_sync", "media_requests", "request_method") helper.Copy(up.Int, "history_sync", "media_requests", "request_local_time") helper.Copy(up.Int, "history_sync", "media_requests", "max_async_handle") + helper.Copy(up.Bool, "history_sync", "backwards_on_demand") } type DisplaynameParams struct { diff --git a/pkg/connector/example-config.yaml b/pkg/connector/example-config.yaml index e441f67..564f25e 100644 --- a/pkg/connector/example-config.yaml +++ b/pkg/connector/example-config.yaml @@ -121,3 +121,6 @@ history_sync: request_local_time: 120 # Maximum number of media request responses to handle in parallel per user. max_async_handle: 2 + # Use on-demand history sync requests for fetching older messages? + # This only applies when using the backfill queue, never for forward backfills. + backwards_on_demand: false From b1c89119437dd963844894a607ed8ac4b75e9d25 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 3 Apr 2026 18:16:20 +0300 Subject: [PATCH 127/153] .github: add checklist to bug report template --- .github/ISSUE_TEMPLATE/bug.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug.md b/.github/ISSUE_TEMPLATE/bug.md index 3703df9..c10630f 100644 --- a/.github/ISSUE_TEMPLATE/bug.md +++ b/.github/ISSUE_TEMPLATE/bug.md @@ -7,10 +7,11 @@ type: Bug --- - -It's always best to ask in the Matrix room first, especially if you aren't sure -what details are needed. Issues with insufficient detail will likely just be -ignored or closed immediately. ---> +### Checklist + + + +* [ ] This is an actual bug, not just a setup issue (see the [troubleshooting docs](https://docs.mau.fi/bridges/general/troubleshooting.html) or ask in the Matrix room for setup help). +* [ ] I am certain that sufficient information is included. Ask in the Matrix room first if not. From 2fc79dba1a2f771d8034bda7f965ab197695c8c7 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 8 Apr 2026 19:59:07 +0300 Subject: [PATCH 128/153] dependencies: update mautrix-go --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index a28f7c7..80eacc1 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ tool go.mau.fi/util/cmd/maubuild require ( github.com/lib/pq v1.12.0 github.com/rs/zerolog v1.35.0 - go.mau.fi/util v0.9.7 + go.mau.fi/util v0.9.8-0.20260406161447-0300c476893a go.mau.fi/webp v0.2.0 go.mau.fi/whatsmeow v0.0.0-20260327181659-02ec817e7cf4 golang.org/x/image v0.37.0 @@ -17,7 +17,7 @@ require ( golang.org/x/sync v0.20.0 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.26.5-0.20260331163037-18917f3bdc14 + maunium.net/go/mautrix v0.26.5-0.20260408131844-9db6af36a393 ) require ( diff --git a/go.sum b/go.sum index ff1245a..d6183fc 100644 --- a/go.sum +++ b/go.sum @@ -71,8 +71,8 @@ github.com/yuin/goldmark v1.8.2 h1:kEGpgqJXdgbkhcOgBxkC0X0PmoPG1ZyoZ117rDVp4zE= github.com/yuin/goldmark v1.8.2/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= go.mau.fi/libsignal v0.2.1 h1:vRZG4EzTn70XY6Oh/pVKrQGuMHBkAWlGRC22/85m9L0= go.mau.fi/libsignal v0.2.1/go.mod h1:iVvjrHyfQqWajOUaMEsIfo3IqgVMrhWcPiiEzk7NgoU= -go.mau.fi/util v0.9.7 h1:AWGNbJfz1zRcQOKeOEYhKUG2fT+/26Gy6kyqcH8tnBg= -go.mau.fi/util v0.9.7/go.mod h1:5T2f3ZWZFAGgmFwg3dGw7YK6kIsb9lryDzvynoR98pE= +go.mau.fi/util v0.9.8-0.20260406161447-0300c476893a h1:OQQF3rTJH10l6+dcP0OKnYbNDMBTGoIZZINNJm8QBG8= +go.mau.fi/util v0.9.8-0.20260406161447-0300c476893a/go.mod h1:5T2f3ZWZFAGgmFwg3dGw7YK6kIsb9lryDzvynoR98pE= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= go.mau.fi/whatsmeow v0.0.0-20260327181659-02ec817e7cf4 h1:E4A6eca9vMJQctC9DIfzUIg27TrJ8IrDHgkJwJ8WPUQ= @@ -107,5 +107,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.26.5-0.20260331163037-18917f3bdc14 h1:y+4gtqKBMTtcVUiAeWJnvp88JLo/h3myQPsz1rZfNOY= -maunium.net/go/mautrix v0.26.5-0.20260331163037-18917f3bdc14/go.mod h1:RUSMBPky3jhXB7Ux+AptfkEvFlJ4ajZKCYiXI8YzxVE= +maunium.net/go/mautrix v0.26.5-0.20260408131844-9db6af36a393 h1:mvoL0cW9nbMO6sJjyO1JZvK5C4y1wr7+JjyyU70Lhuw= +maunium.net/go/mautrix v0.26.5-0.20260408131844-9db6af36a393/go.mod h1:MX4DQLiBe0c7sI/wizruqdxHinSOWs42/DYsP9GH7Q4= From 8b16f9d470a4d783c40645176fa5da96ede94678 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 10 Apr 2026 17:34:05 +0300 Subject: [PATCH 129/153] backfill: log when deleting messages --- pkg/connector/backfill.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/pkg/connector/backfill.go b/pkg/connector/backfill.go index f33351f..8a29311 100644 --- a/pkg/connector/backfill.go +++ b/pkg/connector/backfill.go @@ -549,7 +549,17 @@ func (wa *WhatsAppClient) deleteHistorySyncMessages(ctx context.Context, portalJ err = wa.Main.DB.Message.DeleteBetween(ctx, wa.UserLogin.ID, portalJID, newestTS, oldestTS) } if err != nil { - zerolog.Ctx(ctx).Warn().Err(err).Msg("Failed to delete messages from database after backfill") + zerolog.Ctx(ctx).Warn().Err(err). + Stringer("portal_jid", portalJID). + Uint64("newest_ts", newestTS). + Uint64("oldest_ts", oldestTS). + Msg("Failed to delete messages from database after backfill") + } else { + zerolog.Ctx(ctx).Debug(). + Stringer("portal_jid", portalJID). + Uint64("newest_ts", newestTS). + Uint64("oldest_ts", oldestTS). + Msg("Deleted history sync messages from database") } } From 520d7b9f266ff42bb8c7e944878ddbe830f6b694 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 10 Apr 2026 15:00:46 +0300 Subject: [PATCH 130/153] backfill: use new slow fetch flags --- go.mod | 2 +- go.sum | 4 ++-- pkg/connector/backfill.go | 11 +++++++++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 80eacc1..15383af 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( golang.org/x/sync v0.20.0 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.26.5-0.20260408131844-9db6af36a393 + maunium.net/go/mautrix v0.26.5-0.20260410170402-f76d1a0ce631 ) require ( diff --git a/go.sum b/go.sum index d6183fc..c25697d 100644 --- a/go.sum +++ b/go.sum @@ -107,5 +107,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.26.5-0.20260408131844-9db6af36a393 h1:mvoL0cW9nbMO6sJjyO1JZvK5C4y1wr7+JjyyU70Lhuw= -maunium.net/go/mautrix v0.26.5-0.20260408131844-9db6af36a393/go.mod h1:MX4DQLiBe0c7sI/wizruqdxHinSOWs42/DYsP9GH7Q4= +maunium.net/go/mautrix v0.26.5-0.20260410170402-f76d1a0ce631 h1:rx0v5XOqjBIlQ5oyD2tgRu8C2pHGJnkNyFoZcg4YbeU= +maunium.net/go/mautrix v0.26.5-0.20260410170402-f76d1a0ce631/go.mod h1:MX4DQLiBe0c7sI/wizruqdxHinSOWs42/DYsP9GH7Q4= diff --git a/pkg/connector/backfill.go b/pkg/connector/backfill.go index 8a29311..8ce0cc5 100644 --- a/pkg/connector/backfill.go +++ b/pkg/connector/backfill.go @@ -509,7 +509,14 @@ func (wa *WhatsAppClient) FetchMessages(ctx context.Context, params bridgev2.Fet if err != nil { return nil, fmt.Errorf("failed to load messages from database: %w", err) } else if len(messages) == 0 || (len(messages) == 1 && anchorID != "" && messages[0].GetKey().GetID() == anchorID) { - if hasMore { + wa.deleteHistorySyncMessages(ctx, portalJID, 0, 0) + if hasMore && !params.AllowSlowFetch { + return &bridgev2.FetchMessagesResponse{ + MoreRequiresSlowFetch: true, + HasMore: true, + Forward: params.Forward, + }, nil + } else if hasMore { return wa.fetchMessagesFromPhone(ctx, params) } return &bridgev2.FetchMessagesResponse{ @@ -541,7 +548,7 @@ func (wa *WhatsAppClient) FetchMessages(ctx context.Context, params bridgev2.Fet func (wa *WhatsAppClient) deleteHistorySyncMessages(ctx context.Context, portalJID types.JID, newestTS, oldestTS uint64) { var err error - if (newestTS == 0 && oldestTS == 0) || (!wa.Main.Bridge.Config.Backfill.Queue.Enabled && !wa.Main.Bridge.Config.Backfill.WillPaginateManually) { + if (newestTS == 0 && oldestTS == 0) || !wa.Main.Bridge.Config.Backfill.Queue.AnyEnabled() { // If the backfill queue isn't enabled, delete all messages after backfilling a batch. err = wa.Main.DB.Message.DeleteAllInChat(ctx, wa.UserLogin.ID, portalJID) } else { From e218f9c62975035c1d325ec22b6428eca5bec2f2 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 10 Apr 2026 20:12:51 +0300 Subject: [PATCH 131/153] backfill: log number of rows when deleting messages --- pkg/connector/backfill.go | 6 ++++-- pkg/connector/wadb/message.go | 18 ++++++++++++------ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/pkg/connector/backfill.go b/pkg/connector/backfill.go index 8ce0cc5..e20d717 100644 --- a/pkg/connector/backfill.go +++ b/pkg/connector/backfill.go @@ -548,12 +548,13 @@ func (wa *WhatsAppClient) FetchMessages(ctx context.Context, params bridgev2.Fet func (wa *WhatsAppClient) deleteHistorySyncMessages(ctx context.Context, portalJID types.JID, newestTS, oldestTS uint64) { var err error + var rows int64 if (newestTS == 0 && oldestTS == 0) || !wa.Main.Bridge.Config.Backfill.Queue.AnyEnabled() { // If the backfill queue isn't enabled, delete all messages after backfilling a batch. - err = wa.Main.DB.Message.DeleteAllInChat(ctx, wa.UserLogin.ID, portalJID) + rows, err = wa.Main.DB.Message.DeleteAllInChat(ctx, wa.UserLogin.ID, portalJID) } else { // Otherwise just delete the messages that got backfilled - err = wa.Main.DB.Message.DeleteBetween(ctx, wa.UserLogin.ID, portalJID, newestTS, oldestTS) + rows, err = wa.Main.DB.Message.DeleteBetween(ctx, wa.UserLogin.ID, portalJID, newestTS, oldestTS) } if err != nil { zerolog.Ctx(ctx).Warn().Err(err). @@ -566,6 +567,7 @@ func (wa *WhatsAppClient) deleteHistorySyncMessages(ctx context.Context, portalJ Stringer("portal_jid", portalJID). Uint64("newest_ts", newestTS). Uint64("oldest_ts", oldestTS). + Int64("rows_affected", rows). Msg("Deleted history sync messages from database") } } diff --git a/pkg/connector/wadb/message.go b/pkg/connector/wadb/message.go index f2450c1..4b16002 100644 --- a/pkg/connector/wadb/message.go +++ b/pkg/connector/wadb/message.go @@ -116,9 +116,12 @@ func (mq *MessageQuery) GetBetween(ctx context.Context, loginID networkid.UserLo AsList() } -func (mq *MessageQuery) DeleteBetween(ctx context.Context, loginID networkid.UserLoginID, chatJID types.JID, before, after uint64) error { - _, err := mq.Exec(ctx, deleteHistorySyncMessagesBetweenQuery, mq.BridgeID, loginID, chatJID, before, after) - return err +func (mq *MessageQuery) DeleteBetween(ctx context.Context, loginID networkid.UserLoginID, chatJID types.JID, before, after uint64) (int64, error) { + res, err := mq.Exec(ctx, deleteHistorySyncMessagesBetweenQuery, mq.BridgeID, loginID, chatJID, before, after) + if err != nil { + return 0, err + } + return res.RowsAffected() } func (mq *MessageQuery) DeleteAll(ctx context.Context, loginID networkid.UserLoginID) error { @@ -126,9 +129,12 @@ func (mq *MessageQuery) DeleteAll(ctx context.Context, loginID networkid.UserLog return err } -func (mq *MessageQuery) DeleteAllInChat(ctx context.Context, loginID networkid.UserLoginID, chatJID types.JID) error { - _, err := mq.Exec(ctx, deleteHistorySyncMessagesForPortalQuery, mq.BridgeID, loginID, chatJID) - return err +func (mq *MessageQuery) DeleteAllInChat(ctx context.Context, loginID networkid.UserLoginID, chatJID types.JID) (int64, error) { + res, err := mq.Exec(ctx, deleteHistorySyncMessagesForPortalQuery, mq.BridgeID, loginID, chatJID) + if err != nil { + return 0, err + } + return res.RowsAffected() } func (mq *MessageQuery) ConversationHasMessages(ctx context.Context, loginID networkid.UserLoginID, chatJID types.JID) (exists bool, err error) { From 6510ff1ffd98479d472f0b1b43d3f08e89967b4f Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 13 Apr 2026 12:34:32 +0300 Subject: [PATCH 132/153] backfill: do initial backfill even if only bootstrap is received --- pkg/connector/backfill.go | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/pkg/connector/backfill.go b/pkg/connector/backfill.go index e20d717..90c1316 100644 --- a/pkg/connector/backfill.go +++ b/pkg/connector/backfill.go @@ -138,7 +138,7 @@ func (wa *WhatsAppClient) downloadAndSaveWAHistorySyncData(ctx context.Context, return } err = wa.Main.DB.DoTxn(ctx, nil, func(ctx context.Context) (innerErr error) { - resetTimer, innerErr = wa.handleWAHistorySync(ctx, evt, blob, true) + innerErr = wa.handleWAHistorySync(ctx, evt, blob, true) if innerErr != nil { return } @@ -151,6 +151,9 @@ func (wa *WhatsAppClient) downloadAndSaveWAHistorySyncData(ctx context.Context, if err != nil { log.Err(err).Msg("Failed to store history sync notification data") } else { + resetTimer = blob.GetSyncType() == waHistorySync.HistorySync_INITIAL_BOOTSTRAP || + blob.GetSyncType() == waHistorySync.HistorySync_RECENT || + blob.GetSyncType() == waHistorySync.HistorySync_FULL err = wa.Client.DeleteMedia(ctx, whatsmeow.MediaHistory, evt.GetDirectPath(), evt.GetFileEncSHA256(), evt.GetEncHandle()) if err != nil { log.Err(err).Msg("Failed to delete history sync blob from server") @@ -166,9 +169,9 @@ func (wa *WhatsAppClient) handleWAHistorySync( notif *waE2E.HistorySyncNotification, evt *waHistorySync.HistorySync, stopOnError bool, -) (bool, error) { +) error { if evt == nil || evt.SyncType == nil { - return false, nil + return nil } log := wa.UserLogin.Log.With(). Str("action", "store history sync"). @@ -193,7 +196,7 @@ func (wa *WhatsAppClient) handleWAHistorySync( Int("recent_sticker_count", len(evt.GetRecentStickers())). Int("past_participant_count", len(evt.GetPastParticipants())). Msg("Ignoring history sync") - return false, nil + return nil } log.Info(). Int("conversation_count", len(evt.GetConversations())). @@ -309,7 +312,7 @@ func (wa *WhatsAppClient) handleWAHistorySync( err = wa.Main.DB.Conversation.Put(ctx, wadb.NewConversation(wa.UserLogin.ID, jid, conv, maxTime)) if err != nil { if stopOnError { - return false, fmt.Errorf("failed to save conversation metadata for %s: %w", jid, err) + return fmt.Errorf("failed to save conversation metadata for %s: %w", jid, err) } log.Err(err).Msg("Failed to save conversation metadata") continue @@ -317,7 +320,7 @@ func (wa *WhatsAppClient) handleWAHistorySync( err = wa.Main.DB.Message.Put(ctx, wa.UserLogin.ID, jid, messages) if err != nil { if stopOnError { - return false, fmt.Errorf("failed to save messages in %s: %w", jid, err) + return fmt.Errorf("failed to save messages in %s: %w", jid, err) } log.Err(err).Msg("Failed to save messages") failedToSaveTotal += len(messages) @@ -327,7 +330,7 @@ func (wa *WhatsAppClient) handleWAHistorySync( err = wa.Main.Bridge.DB.BackfillTask.MarkNotDone(ctx, wa.makeWAPortalKey(jid), wa.UserLogin.ID) if err != nil { if stopOnError { - return false, fmt.Errorf("failed to mark backfill task as not done for %s: %w", jid, err) + return fmt.Errorf("failed to mark backfill task as not done for %s: %w", jid, err) } log.Err(err).Msg("Failed to mark backfill task as not done") } @@ -339,9 +342,7 @@ func (wa *WhatsAppClient) handleWAHistorySync( Int("total_message_count", totalMessageCount). Dur("duration", time.Since(start)). Msg("Finished storing history sync") - resetTimer := evt.GetSyncType() == waHistorySync.HistorySync_RECENT || - evt.GetSyncType() == waHistorySync.HistorySync_FULL - return resetTimer, nil + return nil } func (wa *WhatsAppClient) createPortalsFromHistorySync(ctx context.Context) { From 3dc3c1a603e64a089f617537406a6fcbe74fb4fe Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 14 Apr 2026 14:38:18 +0300 Subject: [PATCH 133/153] dependencies: update --- go.mod | 6 +++--- go.sum | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 15383af..dfd3692 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module go.mau.fi/mautrix-whatsapp go 1.25.0 -toolchain go1.26.1 +toolchain go1.26.2 tool go.mau.fi/util/cmd/maubuild @@ -11,13 +11,13 @@ require ( github.com/rs/zerolog v1.35.0 go.mau.fi/util v0.9.8-0.20260406161447-0300c476893a go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20260327181659-02ec817e7cf4 + go.mau.fi/whatsmeow v0.0.0-20260410162419-b95d92207080 golang.org/x/image v0.37.0 golang.org/x/net v0.52.0 golang.org/x/sync v0.20.0 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.26.5-0.20260410170402-f76d1a0ce631 + maunium.net/go/mautrix v0.26.5-0.20260413182302-f3fab8d38470 ) require ( diff --git a/go.sum b/go.sum index c25697d..a7d7ef6 100644 --- a/go.sum +++ b/go.sum @@ -75,8 +75,8 @@ go.mau.fi/util v0.9.8-0.20260406161447-0300c476893a h1:OQQF3rTJH10l6+dcP0OKnYbND go.mau.fi/util v0.9.8-0.20260406161447-0300c476893a/go.mod h1:5T2f3ZWZFAGgmFwg3dGw7YK6kIsb9lryDzvynoR98pE= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20260327181659-02ec817e7cf4 h1:E4A6eca9vMJQctC9DIfzUIg27TrJ8IrDHgkJwJ8WPUQ= -go.mau.fi/whatsmeow v0.0.0-20260327181659-02ec817e7cf4/go.mod h1:mXCRFyPEPn4jqWz6Afirn8vY7DpHCPnlKq6I2cWwFHM= +go.mau.fi/whatsmeow v0.0.0-20260410162419-b95d92207080 h1:j7D8kKa7A1n9kUTmVqHfwHNtoSkgM7FsiSyko/Tye5o= +go.mau.fi/whatsmeow v0.0.0-20260410162419-b95d92207080/go.mod h1:mXCRFyPEPn4jqWz6Afirn8vY7DpHCPnlKq6I2cWwFHM= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= @@ -107,5 +107,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.26.5-0.20260410170402-f76d1a0ce631 h1:rx0v5XOqjBIlQ5oyD2tgRu8C2pHGJnkNyFoZcg4YbeU= -maunium.net/go/mautrix v0.26.5-0.20260410170402-f76d1a0ce631/go.mod h1:MX4DQLiBe0c7sI/wizruqdxHinSOWs42/DYsP9GH7Q4= +maunium.net/go/mautrix v0.26.5-0.20260413182302-f3fab8d38470 h1:vehV8Ev2TzpV5DH9ToCxt43svkXRKcn/kJaZ4mNvRFQ= +maunium.net/go/mautrix v0.26.5-0.20260413182302-f3fab8d38470/go.mod h1:MX4DQLiBe0c7sI/wizruqdxHinSOWs42/DYsP9GH7Q4= From 32ad31902ef24730e5dcf29a0f8f6fab2414c67f Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 16 Apr 2026 16:44:29 +0300 Subject: [PATCH 134/153] Bump version to v26.04 --- CHANGELOG.md | 7 +++++ cmd/mautrix-whatsapp/main.go | 2 +- go.mod | 26 +++++++++--------- go.sum | 52 ++++++++++++++++++------------------ 4 files changed, 47 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3cefd6d..892be2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# v26.04 + +* Added support for @room mentions in both directions. +* Changed initial backfill to happen even if WhatsApp doesn't send full history. +* Fixed panic when handling updates to unknown polls from WhatsApp. +* Fixed some background loops not stopping when a user is logged out. + # v26.03 * Added option to save outgoing messages in the database to allow encryption diff --git a/cmd/mautrix-whatsapp/main.go b/cmd/mautrix-whatsapp/main.go index e64f02f..b97bc7f 100644 --- a/cmd/mautrix-whatsapp/main.go +++ b/cmd/mautrix-whatsapp/main.go @@ -18,7 +18,7 @@ var m = mxmain.BridgeMain{ Name: "mautrix-whatsapp", URL: "https://github.com/mautrix/whatsapp", Description: "A Matrix-WhatsApp puppeting bridge.", - Version: "26.03", + Version: "26.04", SemCalVer: true, Connector: &connector.WhatsAppConnector{}, } diff --git a/go.mod b/go.mod index dfd3692..9aaca46 100644 --- a/go.mod +++ b/go.mod @@ -7,17 +7,17 @@ toolchain go1.26.2 tool go.mau.fi/util/cmd/maubuild require ( - github.com/lib/pq v1.12.0 + github.com/lib/pq v1.12.3 github.com/rs/zerolog v1.35.0 - go.mau.fi/util v0.9.8-0.20260406161447-0300c476893a + go.mau.fi/util v0.9.8 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20260410162419-b95d92207080 - golang.org/x/image v0.37.0 - golang.org/x/net v0.52.0 + go.mau.fi/whatsmeow v0.0.0-20260416104156-3ff20cd3462a + golang.org/x/image v0.39.0 + golang.org/x/net v0.53.0 golang.org/x/sync v0.20.0 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.26.5-0.20260413182302-f3fab8d38470 + maunium.net/go/mautrix v0.27.0 ) require ( @@ -30,8 +30,8 @@ require ( github.com/kr/pretty v0.3.1 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-sqlite3 v1.14.37 // indirect - github.com/petermattis/goid v0.0.0-20260226131333-17d1149c6ac6 // indirect + github.com/mattn/go-sqlite3 v1.14.42 // indirect + github.com/petermattis/goid v0.0.0-20260330135022-df67b199bc81 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/rs/xid v1.6.0 // indirect github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect @@ -43,11 +43,11 @@ require ( github.com/yuin/goldmark v1.8.2 // indirect go.mau.fi/libsignal v0.2.1 // indirect go.mau.fi/zeroconfig v0.2.0 // indirect - golang.org/x/crypto v0.49.0 // indirect - golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 // indirect - golang.org/x/mod v0.34.0 // indirect - golang.org/x/sys v0.42.0 // indirect - golang.org/x/text v0.35.0 // indirect + golang.org/x/crypto v0.50.0 // indirect + golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f // indirect + golang.org/x/mod v0.35.0 // indirect + golang.org/x/sys v0.43.0 // indirect + golang.org/x/text v0.36.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect maunium.net/go/mauflag v1.0.0 // indirect diff --git a/go.sum b/go.sum index a7d7ef6..dd982b9 100644 --- a/go.sum +++ b/go.sum @@ -28,16 +28,16 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/lib/pq v1.12.0 h1:mC1zeiNamwKBecjHarAr26c/+d8V5w/u4J0I/yASbJo= -github.com/lib/pq v1.12.0/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= +github.com/lib/pq v1.12.3 h1:tTWxr2YLKwIvK90ZXEw8GP7UFHtcbTtty8zsI+YjrfQ= +github.com/lib/pq v1.12.3/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-sqlite3 v1.14.37 h1:3DOZp4cXis1cUIpCfXLtmlGolNLp2VEqhiB/PARNBIg= -github.com/mattn/go-sqlite3 v1.14.37/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/petermattis/goid v0.0.0-20260226131333-17d1149c6ac6 h1:rh2lKw/P/EqHa724vYH2+VVQ1YnW4u6EOXl0PMAovZE= -github.com/petermattis/goid v0.0.0-20260226131333-17d1149c6ac6/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +github.com/mattn/go-sqlite3 v1.14.42 h1:MigqEP4ZmHw3aIdIT7T+9TLa90Z6smwcthx+Azv4Cgo= +github.com/mattn/go-sqlite3 v1.14.42/go.mod h1:pjEuOr8IwzLJP2MfGeTb0A35jauH+C2kbHKBr7yXKVQ= +github.com/petermattis/goid v0.0.0-20260330135022-df67b199bc81 h1:WDsQxOJDy0N1VRAjXLpi8sCEZRSGarLWQevDxpTBRrM= +github.com/petermattis/goid v0.0.0-20260330135022-df67b199bc81/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -71,31 +71,31 @@ github.com/yuin/goldmark v1.8.2 h1:kEGpgqJXdgbkhcOgBxkC0X0PmoPG1ZyoZ117rDVp4zE= github.com/yuin/goldmark v1.8.2/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= go.mau.fi/libsignal v0.2.1 h1:vRZG4EzTn70XY6Oh/pVKrQGuMHBkAWlGRC22/85m9L0= go.mau.fi/libsignal v0.2.1/go.mod h1:iVvjrHyfQqWajOUaMEsIfo3IqgVMrhWcPiiEzk7NgoU= -go.mau.fi/util v0.9.8-0.20260406161447-0300c476893a h1:OQQF3rTJH10l6+dcP0OKnYbNDMBTGoIZZINNJm8QBG8= -go.mau.fi/util v0.9.8-0.20260406161447-0300c476893a/go.mod h1:5T2f3ZWZFAGgmFwg3dGw7YK6kIsb9lryDzvynoR98pE= +go.mau.fi/util v0.9.8 h1:+/jf8eM2dAT2wx9UidmaneH28r/CSCKCniCyby1qWz8= +go.mau.fi/util v0.9.8/go.mod h1:up/5mbzH2M1pSBNXqRxODn8dg/hEKbLJu92W4/SNAX0= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20260410162419-b95d92207080 h1:j7D8kKa7A1n9kUTmVqHfwHNtoSkgM7FsiSyko/Tye5o= -go.mau.fi/whatsmeow v0.0.0-20260410162419-b95d92207080/go.mod h1:mXCRFyPEPn4jqWz6Afirn8vY7DpHCPnlKq6I2cWwFHM= +go.mau.fi/whatsmeow v0.0.0-20260416104156-3ff20cd3462a h1:/7erOAOkZ5d/k9bghMMQPciR0ypmOsM8wGv7bIwyyZo= +go.mau.fi/whatsmeow v0.0.0-20260416104156-3ff20cd3462a/go.mod h1:B/y3nOUaK8BDJKvyvq6YbLh2UKTCoiA5xQ2sFwbuOWk= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= -golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= -golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= -golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 h1:jiDhWWeC7jfWqR9c/uplMOqJ0sbNlNWv0UkzE0vX1MA= -golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90/go.mod h1:xE1HEv6b+1SCZ5/uscMRjUBKtIxworgEcEi+/n9NQDQ= -golang.org/x/image v0.37.0 h1:ZiRjArKI8GwxZOoEtUfhrBtaCN+4b/7709dlT6SSnQA= -golang.org/x/image v0.37.0/go.mod h1:/3f6vaXC+6CEanU4KJxbcUZyEePbyKbaLoDOe4ehFYY= -golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= -golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= -golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= -golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= +golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= +golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q= +golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f h1:W3F4c+6OLc6H2lb//N1q4WpJkhzJCK5J6kUi1NTVXfM= +golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f/go.mod h1:J1xhfL/vlindoeF/aINzNzt2Bket5bjo9sdOYzOsU80= +golang.org/x/image v0.39.0 h1:skVYidAEVKgn8lZ602XO75asgXBgLj9G/FE3RbuPFww= +golang.org/x/image v0.39.0/go.mod h1:sIbmppfU+xFLPIG0FoVUTvyBMmgng1/XAMhQ2ft0hpA= +golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM= +golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU= +golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA= +golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= -golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= -golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= +golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= +golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= +golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -107,5 +107,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.26.5-0.20260413182302-f3fab8d38470 h1:vehV8Ev2TzpV5DH9ToCxt43svkXRKcn/kJaZ4mNvRFQ= -maunium.net/go/mautrix v0.26.5-0.20260413182302-f3fab8d38470/go.mod h1:MX4DQLiBe0c7sI/wizruqdxHinSOWs42/DYsP9GH7Q4= +maunium.net/go/mautrix v0.27.0 h1:yfEYwoIluVWkofUgbZl9gP4i5nQTF+QNsxtb+r5bKlM= +maunium.net/go/mautrix v0.27.0/go.mod h1:7QpEQiTy6p4LHkXXaZI+N46tGYy8HMhD0JjzZAFoFWs= From 7254783a32bb3361efc716c9571a2b33cfacd9a1 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 24 Apr 2026 12:46:48 +0300 Subject: [PATCH 135/153] backfill: add debug logs for incorrect timestamps from phone --- pkg/connector/backfill.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pkg/connector/backfill.go b/pkg/connector/backfill.go index 90c1316..b4ef181 100644 --- a/pkg/connector/backfill.go +++ b/pkg/connector/backfill.go @@ -246,7 +246,7 @@ func (wa *WhatsAppClient) handleWAHistorySync( return c.Stringer("chat_jid", jid) }) - var minTime, maxTime time.Time + var minTime, maxTime, firstItemTime, lastItemTime time.Time var minTimeIndex, maxTimeIndex int ignoredTypes := 0 @@ -262,6 +262,10 @@ func (wa *WhatsAppClient) handleWAHistorySync( Msg("Dropping historical message due to parse error") continue } + if firstItemTime.IsZero() { + firstItemTime = msgEvt.Info.Timestamp + } + lastItemTime = msgEvt.Info.Timestamp if minTime.IsZero() || msgEvt.Info.Timestamp.Before(minTime) { minTime = msgEvt.Info.Timestamp minTimeIndex = i @@ -294,6 +298,9 @@ func (wa *WhatsAppClient) handleWAHistorySync( Int("lowest_time_index", minTimeIndex). Time("highest_time", maxTime). Int("highest_time_index", maxTimeIndex). + Time("first_item_time", firstItemTime). + Time("last_item_time", lastItemTime). + Bool("highest_time_mismatch", firstItemTime != maxTime). Dict("metadata", zerolog.Dict(). Uint32("ephemeral_expiration", conv.GetEphemeralExpiration()). Int64("ephemeral_setting_timestamp", conv.GetEphemeralSettingTimestamp()). From 8af9a2f4977333d928b73bb9a85aaffc41ccc829 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 28 Apr 2026 18:16:17 +0300 Subject: [PATCH 136/153] msgconv/sticker: parse metadata from received stickers --- go.mod | 8 +- go.sum | 12 +- pkg/connector/directmedia.go | 2 +- pkg/msgconv/wa-media.go | 102 +------------- pkg/msgconv/wa-sticker.go | 266 +++++++++++++++++++++++++++++++++++ 5 files changed, 282 insertions(+), 108 deletions(-) create mode 100644 pkg/msgconv/wa-sticker.go diff --git a/go.mod b/go.mod index 9aaca46..3054e5a 100644 --- a/go.mod +++ b/go.mod @@ -9,15 +9,16 @@ tool go.mau.fi/util/cmd/maubuild require ( github.com/lib/pq v1.12.3 github.com/rs/zerolog v1.35.0 - go.mau.fi/util v0.9.8 + github.com/tidwall/gjson v1.18.0 + go.mau.fi/util v0.9.9-0.20260428124215-c47a7212562e go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20260416104156-3ff20cd3462a + go.mau.fi/whatsmeow v0.0.0-20260427122815-7514259253a7 golang.org/x/image v0.39.0 golang.org/x/net v0.53.0 golang.org/x/sync v0.20.0 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.27.0 + maunium.net/go/mautrix v0.27.1-0.20260428110059-49a05bf06436 ) require ( @@ -35,7 +36,6 @@ require ( github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/rs/xid v1.6.0 // indirect github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect - github.com/tidwall/gjson v1.18.0 // indirect github.com/tidwall/match v1.2.0 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/sjson v1.2.5 // indirect diff --git a/go.sum b/go.sum index dd982b9..ebe18fa 100644 --- a/go.sum +++ b/go.sum @@ -71,12 +71,12 @@ github.com/yuin/goldmark v1.8.2 h1:kEGpgqJXdgbkhcOgBxkC0X0PmoPG1ZyoZ117rDVp4zE= github.com/yuin/goldmark v1.8.2/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= go.mau.fi/libsignal v0.2.1 h1:vRZG4EzTn70XY6Oh/pVKrQGuMHBkAWlGRC22/85m9L0= go.mau.fi/libsignal v0.2.1/go.mod h1:iVvjrHyfQqWajOUaMEsIfo3IqgVMrhWcPiiEzk7NgoU= -go.mau.fi/util v0.9.8 h1:+/jf8eM2dAT2wx9UidmaneH28r/CSCKCniCyby1qWz8= -go.mau.fi/util v0.9.8/go.mod h1:up/5mbzH2M1pSBNXqRxODn8dg/hEKbLJu92W4/SNAX0= +go.mau.fi/util v0.9.9-0.20260428124215-c47a7212562e h1:o0O9sLa4CeZbxbgoSqavwaORrt9BB+trOLKBSoGzJ3Q= +go.mau.fi/util v0.9.9-0.20260428124215-c47a7212562e/go.mod h1:up/5mbzH2M1pSBNXqRxODn8dg/hEKbLJu92W4/SNAX0= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20260416104156-3ff20cd3462a h1:/7erOAOkZ5d/k9bghMMQPciR0ypmOsM8wGv7bIwyyZo= -go.mau.fi/whatsmeow v0.0.0-20260416104156-3ff20cd3462a/go.mod h1:B/y3nOUaK8BDJKvyvq6YbLh2UKTCoiA5xQ2sFwbuOWk= +go.mau.fi/whatsmeow v0.0.0-20260427122815-7514259253a7 h1:jEOI4I7kU+MYUNI1L94rhYXhUg8N9+YUNHVY525aYTc= +go.mau.fi/whatsmeow v0.0.0-20260427122815-7514259253a7/go.mod h1:ijfkzOXauA/Vz/htXEMfOAJSUgglribW5oQeYC9tSSg= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= @@ -107,5 +107,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.27.0 h1:yfEYwoIluVWkofUgbZl9gP4i5nQTF+QNsxtb+r5bKlM= -maunium.net/go/mautrix v0.27.0/go.mod h1:7QpEQiTy6p4LHkXXaZI+N46tGYy8HMhD0JjzZAFoFWs= +maunium.net/go/mautrix v0.27.1-0.20260428110059-49a05bf06436 h1:vga9ypiOLJmGguxq4D1aquDPFihOuD99EGPEwva12UI= +maunium.net/go/mautrix v0.27.1-0.20260428110059-49a05bf06436/go.mod h1:4fZ0M0xB5ZtueQI65RilX28J/3794BeK+LaCg4U61Jk= diff --git a/pkg/connector/directmedia.go b/pkg/connector/directmedia.go index a4a3d73..10cdbbb 100644 --- a/pkg/connector/directmedia.go +++ b/pkg/connector/directmedia.go @@ -203,7 +203,7 @@ func (wa *WhatsAppConnector) downloadMessageDirectMedia(ctx context.Context, par return nil, fmt.Errorf("failed to seek to start of sticker zip: %w", err) } else if zipData, err := io.ReadAll(f); err != nil { return nil, fmt.Errorf("failed to read sticker zip: %w", err) - } else if data, err := msgconv.ExtractAnimatedSticker(zipData); err != nil { + } else if data, _, err := msgconv.ExtractAnimatedSticker(zipData); err != nil { return nil, fmt.Errorf("failed to extract animated sticker: %w %x", err, zipData) } else if _, err := f.WriteAt(data, 0); err != nil { return nil, fmt.Errorf("failed to write animated sticker to file: %w", err) diff --git a/pkg/msgconv/wa-media.go b/pkg/msgconv/wa-media.go index 9a1ceb3..d1b5332 100644 --- a/pkg/msgconv/wa-media.go +++ b/pkg/msgconv/wa-media.go @@ -17,8 +17,6 @@ package msgconv import ( - "archive/zip" - "bytes" "context" "encoding/json" "errors" @@ -26,15 +24,11 @@ import ( "io" "net/http" "os" - "path/filepath" - "strconv" "strings" "github.com/rs/zerolog" "go.mau.fi/util/exmime" "go.mau.fi/util/exslices" - "go.mau.fi/util/lottie" - "go.mau.fi/util/random" "go.mau.fi/whatsmeow" "go.mau.fi/whatsmeow/proto/waE2E" "go.mau.fi/whatsmeow/types" @@ -198,7 +192,9 @@ type PreparedMedia struct { } func (pm *PreparedMedia) FillFileName() *PreparedMedia { - if pm.FileName == "" { + if pm.Type == event.EventSticker { + pm.FileName = "" + } else if pm.FileName == "" { pm.FileName = strings.TrimPrefix(string(pm.MsgType), "m.") + exmime.ExtensionFromMimetype(pm.Info.MimeType) } return pm @@ -287,9 +283,6 @@ func prepareMediaMessage(rawMsg MediaMessage) *PreparedMedia { case *waE2E.StickerMessage: data.Type = event.EventSticker data.FileName = "sticker" + exmime.ExtensionFromMimetype(msg.GetMimetype()) - if msg.GetMimetype() == "application/was" && data.FileName == "sticker" { - data.FileName = "sticker.json" - } if data.Info.Width == data.Info.Height { data.Info.Width = WhatsAppStickerSize data.Info.Height = WhatsAppStickerSize @@ -397,6 +390,8 @@ func (mc *MessageConverter) reuploadWhatsAppAttachment( if err != nil { return err } + } else if part.Type == event.EventSticker && part.Info.MimeType == "image/webp" { + mc.fillWebPStickerInfo(ctx, part, data) } if part.Info.MimeType == "" { part.Info.MimeType = http.DetectContentType(data) @@ -425,68 +420,6 @@ func (mc *MessageConverter) reuploadWhatsAppAttachment( return nil } -func (mc *MessageConverter) extractAnimatedSticker(fileInfo *PreparedMedia, data []byte) ([]byte, error) { - data, err := ExtractAnimatedSticker(data) - if err != nil { - return nil, err - } - fileInfo.Info.MimeType = "video/lottie+json" - fileInfo.FileName = "sticker.json" - return data, nil -} - -func (mc *MessageConverter) convertAnimatedSticker(ctx context.Context, fileInfo *PreparedMedia, data []byte) ([]byte, []byte, *event.FileInfo, error) { - data, err := mc.extractAnimatedSticker(fileInfo, data) - if err != nil { - return nil, nil, nil, err - } - c := mc.AnimatedStickerConfig - if c.Target == "disable" { - return data, nil, nil, nil - } else if !lottie.Supported() { - zerolog.Ctx(ctx).Warn().Msg("Animated sticker conversion is enabled, but lottieconverter is not installed") - return data, nil, nil, nil - } - input := bytes.NewReader(data) - fileInfo.Info.MimeType = "image/" + c.Target - fileInfo.FileName = "sticker." + c.Target - switch c.Target { - case "png": - var output bytes.Buffer - err = lottie.Convert(ctx, input, "", &output, c.Target, c.Args.Width, c.Args.Height, "1") - return output.Bytes(), nil, nil, err - case "gif": - var output bytes.Buffer - err = lottie.Convert(ctx, input, "", &output, c.Target, c.Args.Width, c.Args.Height, strconv.Itoa(c.Args.FPS)) - return output.Bytes(), nil, nil, err - case "webm", "webp": - tmpFile := filepath.Join(os.TempDir(), fmt.Sprintf("mautrix-whatsapp-lottieconverter-%s.%s", random.String(10), c.Target)) - defer func() { - _ = os.Remove(tmpFile) - }() - thumbnailData, err := lottie.FFmpegConvert(ctx, input, tmpFile, c.Args.Width, c.Args.Height, c.Args.FPS) - if err != nil { - return nil, nil, nil, err - } - data, err = os.ReadFile(tmpFile) - if err != nil { - return nil, nil, nil, fmt.Errorf("failed to read converted file: %w", err) - } - var thumbnailInfo *event.FileInfo - if thumbnailData != nil { - thumbnailInfo = &event.FileInfo{ - MimeType: "image/png", - Width: c.Args.Width, - Height: c.Args.Height, - Size: len(thumbnailData), - } - } - return data, thumbnailData, thumbnailInfo, nil - default: - return nil, nil, nil, fmt.Errorf("unsupported target format %s", c.Target) - } -} - func (mc *MessageConverter) makeMediaFailure(ctx context.Context, mediaInfo *PreparedMedia, keys *FailedMediaKeys, err error) *bridgev2.ConvertedMessagePart { logLevel := zerolog.ErrorLevel var extra map[string]any @@ -531,28 +464,3 @@ func (mc *MessageConverter) makeMediaFailure(ctx context.Context, mediaInfo *Pre } return part } - -func ExtractAnimatedSticker(data []byte) ([]byte, error) { - zipReader, err := zip.NewReader(bytes.NewReader(data), int64(len(data))) - if err != nil { - return nil, fmt.Errorf("failed to read sticker zip: %w", err) - } - animationFile, err := zipReader.Open("animation/animation.json") - if err != nil { - return nil, fmt.Errorf("failed to open animation.json: %w", err) - } - animationFileInfo, err := animationFile.Stat() - if err != nil { - _ = animationFile.Close() - return nil, fmt.Errorf("failed to stat animation.json: %w", err) - } else if animationFileInfo.Size() > uploadFileThreshold { - _ = animationFile.Close() - return nil, fmt.Errorf("animation.json is too large (%.2f MiB)", float64(animationFileInfo.Size())/1024/1024) - } - data, err = io.ReadAll(animationFile) - _ = animationFile.Close() - if err != nil { - return nil, fmt.Errorf("failed to read animation.json: %w", err) - } - return data, nil -} diff --git a/pkg/msgconv/wa-sticker.go b/pkg/msgconv/wa-sticker.go new file mode 100644 index 0000000..fef111a --- /dev/null +++ b/pkg/msgconv/wa-sticker.go @@ -0,0 +1,266 @@ +// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge. +// Copyright (C) 2026 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 . + +package msgconv + +import ( + "archive/zip" + "bytes" + "context" + "encoding/binary" + "encoding/json" + "fmt" + "io" + "os" + "path/filepath" + "strconv" + "strings" + + "github.com/rs/zerolog" + "github.com/tidwall/gjson" + "go.mau.fi/util/exstrings" + "go.mau.fi/util/lottie" + "go.mau.fi/util/random" + "maunium.net/go/mautrix/event" +) + +type StickerMetadata struct { + StickerPackID string `json:"sticker-pack-id"` + AccessibilityText string `json:"accessibility-text"` + Emojis []string `json:"emojis"` + IsFirstPartySticker int `json:"is-first-party-sticker"` +} + +func (sm *StickerMetadata) ToMatrix(content *event.MessageEventContent) { + if sm == nil { + return + } + if sm.StickerPackID != "" { + content.Info.BridgedSticker = &event.BridgedSticker{ + Network: StickerSourceID, + PackURL: StickerPackURLPrefix + sm.StickerPackID, + } + if len(sm.Emojis) > 0 { + content.Info.BridgedSticker.Emoji = sm.Emojis[0] + } + } + if sm.AccessibilityText != "" { + content.Body = sm.AccessibilityText + } else if len(sm.Emojis) > 0 { + content.Body = strings.Join(sm.Emojis, " ") + } +} + +const StickerSourceID = "whatsapp" +const StickerPackURLPrefix = "https://wa.me/stickerpack/" + +func ExtractAnimatedSticker(data []byte) ([]byte, *StickerMetadata, error) { + zipReader, err := zip.NewReader(bytes.NewReader(data), int64(len(data))) + if err != nil { + return nil, nil, fmt.Errorf("failed to read sticker zip: %w", err) + } + animationFile, err := zipReader.Open("animation/animation.json") + if err != nil { + return nil, nil, fmt.Errorf("failed to open animation.json: %w", err) + } + animationFileInfo, err := animationFile.Stat() + if err != nil { + _ = animationFile.Close() + return nil, nil, fmt.Errorf("failed to stat animation.json: %w", err) + } else if animationFileInfo.Size() > uploadFileThreshold { + _ = animationFile.Close() + return nil, nil, fmt.Errorf("animation.json is too large (%.2f MiB)", float64(animationFileInfo.Size())/1024/1024) + } + data, err = io.ReadAll(animationFile) + _ = animationFile.Close() + if err != nil { + return nil, nil, fmt.Errorf("failed to read animation.json: %w", err) + } + var meta StickerMetadata + metaFile, err := zipReader.Open("animation/animation.json.overridden_metadata") + if err == nil { + _ = json.NewDecoder(metaFile).Decode(&meta) + _ = metaFile.Close() + } + if meta.StickerPackID == "" { + res := gjson.GetBytes(data, "metadata.customProps") + if res.IsObject() { + _ = json.Unmarshal(exstrings.UnsafeBytes(res.Raw), &meta) + } + } + return data, &meta, nil +} + +func (mc *MessageConverter) extractAnimatedSticker(fileInfo *PreparedMedia, data []byte) ([]byte, error) { + data, meta, err := ExtractAnimatedSticker(data) + if err != nil { + return nil, err + } + meta.ToMatrix(fileInfo.MessageEventContent) + fileInfo.Info.MimeType = "video/lottie+json" + fileInfo.FileName = "sticker.json" + return data, nil +} + +func (mc *MessageConverter) convertAnimatedSticker(ctx context.Context, fileInfo *PreparedMedia, data []byte) ([]byte, []byte, *event.FileInfo, error) { + data, err := mc.extractAnimatedSticker(fileInfo, data) + if err != nil { + return nil, nil, nil, err + } + c := mc.AnimatedStickerConfig + if c.Target == "disable" { + return data, nil, nil, nil + } else if !lottie.Supported() { + zerolog.Ctx(ctx).Warn().Msg("Animated sticker conversion is enabled, but lottieconverter is not installed") + return data, nil, nil, nil + } + input := bytes.NewReader(data) + fileInfo.Info.MimeType = "image/" + c.Target + fileInfo.FileName = "sticker." + c.Target + switch c.Target { + case "png": + var output bytes.Buffer + err = lottie.Convert(ctx, input, "", &output, c.Target, c.Args.Width, c.Args.Height, "1") + return output.Bytes(), nil, nil, err + case "gif": + var output bytes.Buffer + err = lottie.Convert(ctx, input, "", &output, c.Target, c.Args.Width, c.Args.Height, strconv.Itoa(c.Args.FPS)) + return output.Bytes(), nil, nil, err + case "webm", "webp": + tmpFile := filepath.Join(os.TempDir(), fmt.Sprintf("mautrix-whatsapp-lottieconverter-%s.%s", random.String(10), c.Target)) + defer func() { + _ = os.Remove(tmpFile) + }() + thumbnailData, err := lottie.FFmpegConvert(ctx, input, tmpFile, c.Args.Width, c.Args.Height, c.Args.FPS) + if err != nil { + return nil, nil, nil, err + } + data, err = os.ReadFile(tmpFile) + if err != nil { + return nil, nil, nil, fmt.Errorf("failed to read converted file: %w", err) + } + var thumbnailInfo *event.FileInfo + if thumbnailData != nil { + thumbnailInfo = &event.FileInfo{ + MimeType: "image/png", + Width: c.Args.Width, + Height: c.Args.Height, + Size: len(thumbnailData), + } + } + return data, thumbnailData, thumbnailInfo, nil + default: + return nil, nil, nil, fmt.Errorf("unsupported target format %s", c.Target) + } +} + +func (mc *MessageConverter) fillWebPStickerInfo(ctx context.Context, fileInfo *PreparedMedia, data []byte) { + meta, err := extractWebPStickerMetadata(data) + if err != nil { + zerolog.Ctx(ctx).Debug().Err(err).Msg("Failed to extract webp sticker metadata") + return + } + meta.ToMatrix(fileInfo.MessageEventContent) +} + +// stickerMetadataEXIFTag is the custom EXIF tag WhatsApp uses to embed +// sticker pack metadata as a JSON object inside non-animated webp stickers. +const stickerMetadataEXIFTag = 0x5741 + +// extractWebPStickerMetadata parses the WhatsApp sticker pack metadata JSON +// embedded in EXIF tag 0x5741 of a non-animated webp sticker. +func extractWebPStickerMetadata(data []byte) (*StickerMetadata, error) { + exif, err := findWebPChunk(data, "EXIF") + if err != nil { + return nil, err + } + raw, err := findEXIFTagValue(exif, stickerMetadataEXIFTag) + if err != nil { + return nil, err + } + var meta StickerMetadata + err = json.Unmarshal(raw, &meta) + if err != nil { + return nil, fmt.Errorf("failed to parse sticker metadata JSON: %w", err) + } + return &meta, nil +} + +func findWebPChunk(data []byte, chunkType string) ([]byte, error) { + if len(data) < 12 || string(data[0:4]) != "RIFF" || string(data[8:12]) != "WEBP" { + return nil, fmt.Errorf("not a webp file") + } + for pos := 12; pos+8 <= len(data); { + size := binary.LittleEndian.Uint32(data[pos+4 : pos+8]) + start := pos + 8 + end := start + int(size) + if end > len(data) { + return nil, fmt.Errorf("webp chunk %q extends past end of file", data[pos:pos+4]) + } + if string(data[pos:pos+4]) == chunkType { + return data[start:end], nil + } + pos = end + if pos%2 != 0 { + pos++ + } + } + return nil, fmt.Errorf("webp chunk %q not found", chunkType) +} + +func findEXIFTagValue(exif []byte, tag uint16) ([]byte, error) { + if len(exif) < 8 { + return nil, fmt.Errorf("exif data too short") + } + var bo binary.ByteOrder + switch string(exif[0:2]) { + case "II": + bo = binary.LittleEndian + case "MM": + bo = binary.BigEndian + default: + return nil, fmt.Errorf("invalid TIFF byte order %q", exif[0:2]) + } + if bo.Uint16(exif[2:4]) != 0x002A { + return nil, fmt.Errorf("invalid TIFF magic") + } + ifdOffset := int(bo.Uint32(exif[4:8])) + if ifdOffset < 0 || ifdOffset+2 > len(exif) { + return nil, fmt.Errorf("IFD offset out of range") + } + count := int(bo.Uint16(exif[ifdOffset : ifdOffset+2])) + entries := ifdOffset + 2 + if entries+count*12 > len(exif) { + return nil, fmt.Errorf("IFD entries out of range") + } + for i := 0; i < count; i++ { + entry := exif[entries+i*12 : entries+(i+1)*12] + if bo.Uint16(entry[0:2]) != tag { + continue + } + // Tag 0x5741 stores JSON as type 7 (UNDEFINED), where size == count bytes. + size := int(bo.Uint32(entry[4:8])) + if size <= 4 { + return entry[8 : 8+size], nil + } + offset := int(bo.Uint32(entry[8:12])) + if offset+size > len(exif) { + return nil, fmt.Errorf("exif tag value out of range") + } + return exif[offset : offset+size], nil + } + return nil, fmt.Errorf("exif tag 0x%04x not found", tag) +} From 3f568f0133a50e7b64b651a91d8dae59e7558ff7 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 29 Apr 2026 09:10:15 +0300 Subject: [PATCH 137/153] dependencies: update mautrix-go --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 3054e5a..f499a24 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( golang.org/x/sync v0.20.0 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.27.1-0.20260428110059-49a05bf06436 + maunium.net/go/mautrix v0.27.1-0.20260429060852-d7aad0e862c7 ) require ( diff --git a/go.sum b/go.sum index ebe18fa..38542dc 100644 --- a/go.sum +++ b/go.sum @@ -107,5 +107,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.27.1-0.20260428110059-49a05bf06436 h1:vga9ypiOLJmGguxq4D1aquDPFihOuD99EGPEwva12UI= -maunium.net/go/mautrix v0.27.1-0.20260428110059-49a05bf06436/go.mod h1:4fZ0M0xB5ZtueQI65RilX28J/3794BeK+LaCg4U61Jk= +maunium.net/go/mautrix v0.27.1-0.20260429060852-d7aad0e862c7 h1:ZL/dTgBuj7ZzH543brFUvxZo2lJGsCMBvnfKIvjdHC4= +maunium.net/go/mautrix v0.27.1-0.20260429060852-d7aad0e862c7/go.mod h1:4fZ0M0xB5ZtueQI65RilX28J/3794BeK+LaCg4U61Jk= From 0632478ce0deb14fa0e6860655cfc123a5626100 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 29 Apr 2026 19:04:18 +0300 Subject: [PATCH 138/153] dependencies: update mautrix-go --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index f499a24..63b26bc 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( golang.org/x/sync v0.20.0 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.27.1-0.20260429060852-d7aad0e862c7 + maunium.net/go/mautrix v0.27.1-0.20260429160319-674a25f9b6ee ) require ( diff --git a/go.sum b/go.sum index 38542dc..44ae65d 100644 --- a/go.sum +++ b/go.sum @@ -107,5 +107,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.27.1-0.20260429060852-d7aad0e862c7 h1:ZL/dTgBuj7ZzH543brFUvxZo2lJGsCMBvnfKIvjdHC4= -maunium.net/go/mautrix v0.27.1-0.20260429060852-d7aad0e862c7/go.mod h1:4fZ0M0xB5ZtueQI65RilX28J/3794BeK+LaCg4U61Jk= +maunium.net/go/mautrix v0.27.1-0.20260429160319-674a25f9b6ee h1:OiKSGPfWLQYir1QmvkMvfo/0Dh78hVo8boGwU0Ub32k= +maunium.net/go/mautrix v0.27.1-0.20260429160319-674a25f9b6ee/go.mod h1:4fZ0M0xB5ZtueQI65RilX28J/3794BeK+LaCg4U61Jk= From 60a46fd81bf72cea06f9dd71152ef7cd019912cb Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 30 Apr 2026 13:23:21 +0300 Subject: [PATCH 139/153] .github: add another item to bug report template [skip ci] --- .github/ISSUE_TEMPLATE/bug.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug.md b/.github/ISSUE_TEMPLATE/bug.md index c10630f..06ba9e8 100644 --- a/.github/ISSUE_TEMPLATE/bug.md +++ b/.github/ISSUE_TEMPLATE/bug.md @@ -11,7 +11,8 @@ type: Bug ### Checklist - + * [ ] This is an actual bug, not just a setup issue (see the [troubleshooting docs](https://docs.mau.fi/bridges/general/troubleshooting.html) or ask in the Matrix room for setup help). * [ ] I am certain that sufficient information is included. Ask in the Matrix room first if not. +* [ ] The bug is still present on the main branch. From 42e83b1ea9f16a916a27ecab60c2ae4b082ab359 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 4 May 2026 17:01:16 +0300 Subject: [PATCH 140/153] dependencies: update --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 63b26bc..2a66a9d 100644 --- a/go.mod +++ b/go.mod @@ -8,17 +8,17 @@ tool go.mau.fi/util/cmd/maubuild require ( github.com/lib/pq v1.12.3 - github.com/rs/zerolog v1.35.0 + github.com/rs/zerolog v1.35.1 github.com/tidwall/gjson v1.18.0 - go.mau.fi/util v0.9.9-0.20260428124215-c47a7212562e + go.mau.fi/util v0.9.9-0.20260501211038-7535d5590b78 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20260427122815-7514259253a7 + go.mau.fi/whatsmeow v0.0.0-20260504140538-51dcc5e33be0 golang.org/x/image v0.39.0 golang.org/x/net v0.53.0 golang.org/x/sync v0.20.0 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.27.1-0.20260429160319-674a25f9b6ee + maunium.net/go/mautrix v0.27.1-0.20260502202615-25947505f4a2 ) require ( diff --git a/go.sum b/go.sum index 44ae65d..e22300a 100644 --- a/go.sum +++ b/go.sum @@ -46,8 +46,8 @@ github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjR github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= -github.com/rs/zerolog v1.35.0 h1:VD0ykx7HMiMJytqINBsKcbLS+BJ4WYjz+05us+LRTdI= -github.com/rs/zerolog v1.35.0/go.mod h1:EjML9kdfa/RMA7h/6z6pYmq1ykOuA8/mjWaEvGI+jcw= +github.com/rs/zerolog v1.35.1 h1:m7xQeoiLIiV0BCEY4Hs+j2NG4Gp2o2KPKmhnnLiazKI= +github.com/rs/zerolog v1.35.1/go.mod h1:EjML9kdfa/RMA7h/6z6pYmq1ykOuA8/mjWaEvGI+jcw= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= @@ -71,12 +71,12 @@ github.com/yuin/goldmark v1.8.2 h1:kEGpgqJXdgbkhcOgBxkC0X0PmoPG1ZyoZ117rDVp4zE= github.com/yuin/goldmark v1.8.2/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= go.mau.fi/libsignal v0.2.1 h1:vRZG4EzTn70XY6Oh/pVKrQGuMHBkAWlGRC22/85m9L0= go.mau.fi/libsignal v0.2.1/go.mod h1:iVvjrHyfQqWajOUaMEsIfo3IqgVMrhWcPiiEzk7NgoU= -go.mau.fi/util v0.9.9-0.20260428124215-c47a7212562e h1:o0O9sLa4CeZbxbgoSqavwaORrt9BB+trOLKBSoGzJ3Q= -go.mau.fi/util v0.9.9-0.20260428124215-c47a7212562e/go.mod h1:up/5mbzH2M1pSBNXqRxODn8dg/hEKbLJu92W4/SNAX0= +go.mau.fi/util v0.9.9-0.20260501211038-7535d5590b78 h1:MRz5RQxXgohVSulsFHqokfZDJzhqk1w+fDQxJksxbZc= +go.mau.fi/util v0.9.9-0.20260501211038-7535d5590b78/go.mod h1:up/5mbzH2M1pSBNXqRxODn8dg/hEKbLJu92W4/SNAX0= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20260427122815-7514259253a7 h1:jEOI4I7kU+MYUNI1L94rhYXhUg8N9+YUNHVY525aYTc= -go.mau.fi/whatsmeow v0.0.0-20260427122815-7514259253a7/go.mod h1:ijfkzOXauA/Vz/htXEMfOAJSUgglribW5oQeYC9tSSg= +go.mau.fi/whatsmeow v0.0.0-20260504140538-51dcc5e33be0 h1:LIqLhtTxsOsJbQn+WQVqQjgVQJgWYRGQptLJG1DN0/Y= +go.mau.fi/whatsmeow v0.0.0-20260504140538-51dcc5e33be0/go.mod h1:ijfkzOXauA/Vz/htXEMfOAJSUgglribW5oQeYC9tSSg= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= @@ -107,5 +107,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.27.1-0.20260429160319-674a25f9b6ee h1:OiKSGPfWLQYir1QmvkMvfo/0Dh78hVo8boGwU0Ub32k= -maunium.net/go/mautrix v0.27.1-0.20260429160319-674a25f9b6ee/go.mod h1:4fZ0M0xB5ZtueQI65RilX28J/3794BeK+LaCg4U61Jk= +maunium.net/go/mautrix v0.27.1-0.20260502202615-25947505f4a2 h1:yuYNi5X7baSlW/rDXlTV1n+x72uMVwPCubNnMG5wrqk= +maunium.net/go/mautrix v0.27.1-0.20260502202615-25947505f4a2/go.mod h1:t9xgVOeRTI3QAX04dBEM6iql+SnOOLdIy2jaKWyL2M0= From e13f63e6b827ad2a0fefc7d3604e771f72891221 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 6 May 2026 13:14:07 +0300 Subject: [PATCH 141/153] dependencies: update --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 2a66a9d..d55b65c 100644 --- a/go.mod +++ b/go.mod @@ -10,15 +10,15 @@ require ( github.com/lib/pq v1.12.3 github.com/rs/zerolog v1.35.1 github.com/tidwall/gjson v1.18.0 - go.mau.fi/util v0.9.9-0.20260501211038-7535d5590b78 + go.mau.fi/util v0.9.9-0.20260505143909-8e67f0d355e0 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20260504140538-51dcc5e33be0 + go.mau.fi/whatsmeow v0.0.0-20260506100936-a763037b215a golang.org/x/image v0.39.0 golang.org/x/net v0.53.0 golang.org/x/sync v0.20.0 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.27.1-0.20260502202615-25947505f4a2 + maunium.net/go/mautrix v0.27.1-0.20260506130904-37580129eaaf ) require ( @@ -31,7 +31,7 @@ require ( github.com/kr/pretty v0.3.1 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-sqlite3 v1.14.42 // indirect + github.com/mattn/go-sqlite3 v1.14.44 // indirect github.com/petermattis/goid v0.0.0-20260330135022-df67b199bc81 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/rs/xid v1.6.0 // indirect diff --git a/go.sum b/go.sum index e22300a..bbef170 100644 --- a/go.sum +++ b/go.sum @@ -34,8 +34,8 @@ github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHP github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-sqlite3 v1.14.42 h1:MigqEP4ZmHw3aIdIT7T+9TLa90Z6smwcthx+Azv4Cgo= -github.com/mattn/go-sqlite3 v1.14.42/go.mod h1:pjEuOr8IwzLJP2MfGeTb0A35jauH+C2kbHKBr7yXKVQ= +github.com/mattn/go-sqlite3 v1.14.44 h1:3VSe+xafpbzsLbdr2AWlAZk9yRHiBhTBakioXaCKTF8= +github.com/mattn/go-sqlite3 v1.14.44/go.mod h1:pjEuOr8IwzLJP2MfGeTb0A35jauH+C2kbHKBr7yXKVQ= github.com/petermattis/goid v0.0.0-20260330135022-df67b199bc81 h1:WDsQxOJDy0N1VRAjXLpi8sCEZRSGarLWQevDxpTBRrM= github.com/petermattis/goid v0.0.0-20260330135022-df67b199bc81/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= @@ -71,12 +71,12 @@ github.com/yuin/goldmark v1.8.2 h1:kEGpgqJXdgbkhcOgBxkC0X0PmoPG1ZyoZ117rDVp4zE= github.com/yuin/goldmark v1.8.2/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= go.mau.fi/libsignal v0.2.1 h1:vRZG4EzTn70XY6Oh/pVKrQGuMHBkAWlGRC22/85m9L0= go.mau.fi/libsignal v0.2.1/go.mod h1:iVvjrHyfQqWajOUaMEsIfo3IqgVMrhWcPiiEzk7NgoU= -go.mau.fi/util v0.9.9-0.20260501211038-7535d5590b78 h1:MRz5RQxXgohVSulsFHqokfZDJzhqk1w+fDQxJksxbZc= -go.mau.fi/util v0.9.9-0.20260501211038-7535d5590b78/go.mod h1:up/5mbzH2M1pSBNXqRxODn8dg/hEKbLJu92W4/SNAX0= +go.mau.fi/util v0.9.9-0.20260505143909-8e67f0d355e0 h1:stkCMpY3ULN6sNrPoRYZ5AQ/kc20a7pmhv6t0sdyVhE= +go.mau.fi/util v0.9.9-0.20260505143909-8e67f0d355e0/go.mod h1:jE9FfhbgEgAwxei6lomO9v8zdCIATcquONUu4vjRwSs= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20260504140538-51dcc5e33be0 h1:LIqLhtTxsOsJbQn+WQVqQjgVQJgWYRGQptLJG1DN0/Y= -go.mau.fi/whatsmeow v0.0.0-20260504140538-51dcc5e33be0/go.mod h1:ijfkzOXauA/Vz/htXEMfOAJSUgglribW5oQeYC9tSSg= +go.mau.fi/whatsmeow v0.0.0-20260506100936-a763037b215a h1:DfD7BXe4m+MIPAe0TjFb8hFUd42CqybeWaTvOH6dMiw= +go.mau.fi/whatsmeow v0.0.0-20260506100936-a763037b215a/go.mod h1:ijfkzOXauA/Vz/htXEMfOAJSUgglribW5oQeYC9tSSg= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= @@ -107,5 +107,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.27.1-0.20260502202615-25947505f4a2 h1:yuYNi5X7baSlW/rDXlTV1n+x72uMVwPCubNnMG5wrqk= -maunium.net/go/mautrix v0.27.1-0.20260502202615-25947505f4a2/go.mod h1:t9xgVOeRTI3QAX04dBEM6iql+SnOOLdIy2jaKWyL2M0= +maunium.net/go/mautrix v0.27.1-0.20260506130904-37580129eaaf h1:AXGEYhQsiQArdtD1XE0NnGIl614j9stHXFmaB+Kb8Sw= +maunium.net/go/mautrix v0.27.1-0.20260506130904-37580129eaaf/go.mod h1:2ANjihDB+wv2UAqJapkRekmNXw7khSisccAkE5Jg3P0= From 0f6e7a522c3ae2ccdcd3928a39e6ede8ef513524 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 6 May 2026 13:23:02 +0300 Subject: [PATCH 142/153] .github: add version command to bug report template --- .github/ISSUE_TEMPLATE/bug.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug.md b/.github/ISSUE_TEMPLATE/bug.md index 06ba9e8..4b3b934 100644 --- a/.github/ISSUE_TEMPLATE/bug.md +++ b/.github/ISSUE_TEMPLATE/bug.md @@ -15,4 +15,4 @@ type: Bug * [ ] This is an actual bug, not just a setup issue (see the [troubleshooting docs](https://docs.mau.fi/bridges/general/troubleshooting.html) or ask in the Matrix room for setup help). * [ ] I am certain that sufficient information is included. Ask in the Matrix room first if not. -* [ ] The bug is still present on the main branch. +* [ ] The bug is still present on the main branch. The `!wa version` command output is: `` From ac2912a14536d80453ec57390465edb05afcb71f Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 6 May 2026 16:03:28 +0300 Subject: [PATCH 143/153] msgconv,directmedia: add support for importing image packs --- go.mod | 2 +- go.sum | 4 +- pkg/connector/capabilities.go | 4 +- pkg/connector/client.go | 11 +++ pkg/connector/directmedia.go | 47 ++++++++-- pkg/connector/handlematrix.go | 1 + pkg/msgconv/from-matrix.go | 38 +++++++- pkg/msgconv/msgconv.go | 11 ++- pkg/msgconv/wa-media.go | 14 ++- pkg/msgconv/wa-sticker.go | 172 +++++++++++++++++++++++++++++++++- pkg/waid/mediaid.go | 42 +++++++++ 11 files changed, 323 insertions(+), 23 deletions(-) diff --git a/go.mod b/go.mod index d55b65c..1f12612 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/tidwall/gjson v1.18.0 go.mau.fi/util v0.9.9-0.20260505143909-8e67f0d355e0 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20260506100936-a763037b215a + go.mau.fi/whatsmeow v0.0.0-20260506122147-6a7198d94d26 golang.org/x/image v0.39.0 golang.org/x/net v0.53.0 golang.org/x/sync v0.20.0 diff --git a/go.sum b/go.sum index bbef170..08791dd 100644 --- a/go.sum +++ b/go.sum @@ -75,8 +75,8 @@ go.mau.fi/util v0.9.9-0.20260505143909-8e67f0d355e0 h1:stkCMpY3ULN6sNrPoRYZ5AQ/k go.mau.fi/util v0.9.9-0.20260505143909-8e67f0d355e0/go.mod h1:jE9FfhbgEgAwxei6lomO9v8zdCIATcquONUu4vjRwSs= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20260506100936-a763037b215a h1:DfD7BXe4m+MIPAe0TjFb8hFUd42CqybeWaTvOH6dMiw= -go.mau.fi/whatsmeow v0.0.0-20260506100936-a763037b215a/go.mod h1:ijfkzOXauA/Vz/htXEMfOAJSUgglribW5oQeYC9tSSg= +go.mau.fi/whatsmeow v0.0.0-20260506122147-6a7198d94d26 h1:DyFksXWn7z/NN+TNJ0DomV1/drWjkyiVuJ6RIiy/bo4= +go.mau.fi/whatsmeow v0.0.0-20260506122147-6a7198d94d26/go.mod h1:ijfkzOXauA/Vz/htXEMfOAJSUgglribW5oQeYC9tSSg= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= diff --git a/pkg/connector/capabilities.go b/pkg/connector/capabilities.go index 202569b..d8b1367 100644 --- a/pkg/connector/capabilities.go +++ b/pkg/connector/capabilities.go @@ -125,10 +125,10 @@ var whatsappCaps = &event.RoomFeatures{ event.CapMsgSticker: { MimeTypes: map[string]event.CapabilitySupportLevel{ "image/webp": event.CapLevelFullySupported, - // TODO see if sending lottie is possible - //"video/lottie+json": event.CapLevelFullySupported, "image/png": event.CapLevelPartialSupport, "image/jpeg": event.CapLevelPartialSupport, + // This will only be accepted if it was imported from WhatsApp + "video/lottie+json": event.CapLevelPartialSupport, }, Caption: event.CapLevelDropped, MaxSize: WAMaxFileSize, diff --git a/pkg/connector/client.go b/pkg/connector/client.go index 367ad14..3f19bf7 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -38,6 +38,7 @@ import ( "maunium.net/go/mautrix/bridgev2" "maunium.net/go/mautrix/bridgev2/networkid" "maunium.net/go/mautrix/bridgev2/status" + "maunium.net/go/mautrix/event" "go.mau.fi/mautrix-whatsapp/pkg/waid" ) @@ -128,6 +129,7 @@ var ( _ bridgev2.PushableNetworkAPI = (*WhatsAppClient)(nil) _ bridgev2.BackgroundSyncingNetworkAPI = (*WhatsAppClient)(nil) _ bridgev2.ChatViewingNetworkAPI = (*WhatsAppClient)(nil) + _ bridgev2.StickerImportingNetworkAPI = (*WhatsAppClient)(nil) ) var pushCfg = &bridgev2.PushConfig{ @@ -467,3 +469,12 @@ func (wa *WhatsAppClient) updatePresence(ctx context.Context, presence types.Pre } return err } + +func (wa *WhatsAppClient) DownloadImagePack(ctx context.Context, url string) (*bridgev2.ImportedImagePack, error) { + return wa.Main.MsgConv.DownloadImagePack(ctx, wa.UserLogin.ID, wa.Client, url) +} + +func (wa *WhatsAppClient) ListImagePacks(ctx context.Context) ([]*event.ImagePackMetadata, error) { + // TODO + return nil, nil +} diff --git a/pkg/connector/directmedia.go b/pkg/connector/directmedia.go index 10cdbbb..ac2fbd2 100644 --- a/pkg/connector/directmedia.go +++ b/pkg/connector/directmedia.go @@ -67,6 +67,8 @@ func (wa *WhatsAppConnector) Download(ctx context.Context, mediaID networkid.Med return wa.downloadMessageDirectMedia(ctx, parsedID, params) } else if parsedID.Avatar != nil { return wa.downloadAvatarDirectMedia(ctx, parsedID, params) + } else if parsedID.Sticker != nil { + return wa.downloadStickerDirectMedia(ctx, parsedID, params) } else { return nil, fmt.Errorf("unexpected media ID parsing result") } @@ -135,8 +137,25 @@ func (wa *WhatsAppConnector) downloadAvatarDirectMedia(ctx context.Context, pars }, nil } +func (wa *WhatsAppConnector) downloadStickerDirectMedia(ctx context.Context, parsedID *waid.ParsedMediaID, params map[string]string) (mediaproxy.GetMediaResponse, error) { + ul := wa.Bridge.GetCachedUserLoginByID(parsedID.UserLogin) + if ul == nil { + return nil, fmt.Errorf("%w: user login %s not found", bridgev2.ErrNotLoggedIn, parsedID.UserLogin) + } + waClient := ul.Client.(*WhatsAppClient) + if waClient.Client == nil { + return nil, fmt.Errorf("no WhatsApp client found on login %s", parsedID.UserLogin) + } + sticker, err := wa.MsgConv.GetCachedSticker(ctx, waClient.Client, parsedID.Sticker.PackID, parsedID.Sticker.FileHash) + if err != nil { + return nil, err + } else if sticker == nil { + return nil, mautrix.MNotFound.WithMessage("Sticker not found in pack") + } + return wa.makeDirectMediaResponse(ctx, waClient, sticker, sticker.MimeType, "", nil, params) +} + func (wa *WhatsAppConnector) downloadMessageDirectMedia(ctx context.Context, parsedID *waid.ParsedMediaID, params map[string]string) (mediaproxy.GetMediaResponse, error) { - log := zerolog.Ctx(ctx) msg, err := wa.Bridge.DB.Message.GetFirstPartByID(ctx, parsedID.UserLogin, parsedID.Message.String()) if err != nil { return nil, fmt.Errorf("failed to get message: %w", err) @@ -174,16 +193,29 @@ func (wa *WhatsAppConnector) downloadMessageDirectMedia(ctx context.Context, par if waClient.Client == nil { return nil, fmt.Errorf("no WhatsApp client found on login") } + return wa.makeDirectMediaResponse(ctx, waClient, keys, keys.MimeType, msg.ID, keys, params) +} + +func (wa *WhatsAppConnector) makeDirectMediaResponse( + ctx context.Context, + waClient *WhatsAppClient, + dm whatsmeow.DownloadableMessage, + mimeType string, + msgID networkid.MessageID, + keys *msgconv.FailedMediaKeys, + params map[string]string, +) (mediaproxy.GetMediaResponse, error) { return &mediaproxy.GetMediaResponseFile{ Callback: func(f *os.File) (*mediaproxy.FileMeta, error) { - err := waClient.Client.DownloadToFile(ctx, keys, f) - if errors.Is(err, whatsmeow.ErrMediaDownloadFailedWith403) || errors.Is(err, whatsmeow.ErrMediaDownloadFailedWith404) || errors.Is(err, whatsmeow.ErrMediaDownloadFailedWith410) || errors.Is(err, whatsmeow.ErrNoURLPresent) { + log := zerolog.Ctx(ctx) + err := waClient.Client.DownloadToFile(ctx, dm, f) + if keys != nil && (errors.Is(err, whatsmeow.ErrMediaDownloadFailedWith403) || errors.Is(err, whatsmeow.ErrMediaDownloadFailedWith404) || errors.Is(err, whatsmeow.ErrMediaDownloadFailedWith410) || errors.Is(err, whatsmeow.ErrNoURLPresent)) { val := params["fi.mau.whatsapp.reload_media"] if val == "false" || (!wa.Config.DirectMediaAutoRequest && val != "true") { return nil, ErrReloadNeeded } log.Trace().Msg("Media not found for direct download, requesting and waiting") - err = waClient.requestAndWaitDirectMedia(ctx, msg.ID, keys) + err = waClient.requestAndWaitDirectMedia(ctx, msgID, keys) if err != nil { log.Trace().Err(err).Msg("Failed to wait for media for direct download") return nil, err @@ -197,8 +229,7 @@ func (wa *WhatsAppConnector) downloadMessageDirectMedia(ctx context.Context, par return nil, err } - mime := keys.MimeType - if mime == "application/was" { + if mimeType == "application/was" { if _, err := f.Seek(0, io.SeekStart); err != nil { return nil, fmt.Errorf("failed to seek to start of sticker zip: %w", err) } else if zipData, err := io.ReadAll(f); err != nil { @@ -210,11 +241,11 @@ func (wa *WhatsAppConnector) downloadMessageDirectMedia(ctx context.Context, par } else if err := f.Truncate(int64(len(data))); err != nil { return nil, fmt.Errorf("failed to truncate animated sticker file: %w", err) } - mime = "video/lottie+json" + mimeType = "video/lottie+json" } return &mediaproxy.FileMeta{ - ContentType: mime, + ContentType: mimeType, }, nil }, }, nil diff --git a/pkg/connector/handlematrix.go b/pkg/connector/handlematrix.go index a0a67fa..b0963ec 100644 --- a/pkg/connector/handlematrix.go +++ b/pkg/connector/handlematrix.go @@ -107,6 +107,7 @@ func (wa *WhatsAppClient) handleConvertedMatrixMessage(ctx context.Context, msg wrappedMsgID2 := waid.MakeMessageID(chatJID, wa.GetStore().GetLID(), req.ID) msg.AddPendingToIgnore(networkid.TransactionID(wrappedMsgID)) msg.AddPendingToIgnore(networkid.TransactionID(wrappedMsgID2)) + zerolog.Ctx(ctx).Trace().Any("payload", waMsg).Msg("Outgoing message payload") resp, err := wa.Client.SendMessage(ctx, chatJID, waMsg, *req) if err != nil { return nil, err diff --git a/pkg/msgconv/from-matrix.go b/pkg/msgconv/from-matrix.go index 49f6048..4eb0a88 100644 --- a/pkg/msgconv/from-matrix.go +++ b/pkg/msgconv/from-matrix.go @@ -19,6 +19,7 @@ package msgconv import ( "bytes" "context" + "encoding/base64" "encoding/json" "errors" "fmt" @@ -201,6 +202,7 @@ func (mc *MessageConverter) constructMediaMessage( FileSHA256: uploaded.FileSHA256, FileLength: proto.Uint64(uploaded.FileLength), URL: proto.String(uploaded.URL), + IsLottie: proto.Bool(mime == "application/was"), }, } case event.MsgAudio: @@ -482,6 +484,17 @@ func (mc *MessageConverter) convertToWebP(img []byte) ([]byte, int, error) { return webpBuffer.Bytes(), size, nil } +func (mc *MessageConverter) getOriginalBridgedSticker(ctx context.Context, info *event.BridgedSticker) (*types.StickerPackItem, error) { + if info == nil || info.Network != StickerSourceID || !strings.HasPrefix(info.PackURL, StickerPackURLPrefix) || info.ID == "" { + return nil, nil + } + fileHash, err := base64.StdEncoding.DecodeString(info.ID) + if err != nil { + return nil, nil + } + return mc.GetCachedSticker(ctx, getClient(ctx), strings.TrimPrefix(info.PackURL, StickerPackURLPrefix), fileHash) +} + func (mc *MessageConverter) reuploadFileToWhatsApp( ctx context.Context, content *event.MessageEventContent, ) (*whatsmeow.UploadResponse, []byte, string, error) { @@ -490,7 +503,21 @@ func (mc *MessageConverter) reuploadFileToWhatsApp( if content.FileName != "" { fileName = content.FileName } - data, err := mc.Bridge.Bot.DownloadMedia(ctx, content.URL, content.File) + var data []byte + var err error + var sticker *types.StickerPackItem + if sticker, err = mc.getOriginalBridgedSticker(ctx, content.Info.BridgedSticker); sticker != nil && sticker.MimeType == "application/was" { + data, err = getClient(ctx).Download(ctx, sticker) + mime = sticker.MimeType + content.Info.Width = sticker.Width + content.Info.Height = sticker.Height + } else { + if err != nil { + zerolog.Ctx(ctx).Warn().Err(err). + Msg("Failed to get original bridged sticker, falling back to downloading from URL") + } + data, err = mc.Bridge.Bot.DownloadMedia(ctx, content.URL, content.File) + } if err != nil { return nil, nil, "", fmt.Errorf("%w: %w", bridgev2.ErrMediaDownloadFailed, err) } @@ -508,7 +535,14 @@ func (mc *MessageConverter) reuploadFileToWhatsApp( case event.MessageType(event.EventSticker.Type): isSticker = true mediaType = whatsmeow.MediaImage - if mime != "image/webp" || content.Info.Width != content.Info.Height { + if mime == "video/lottie+json" { + // This likely won't work + data, err = PackAnimatedSticker(data) + if err != nil { + return nil, nil, mime, fmt.Errorf("%w (packing animated sticker): %w", bridgev2.ErrMediaConvertFailed, err) + } + mime = "application/was" + } else if (mime != "image/webp" || content.Info.Width != content.Info.Height) && mime != "application/was" { var size int data, size, err = mc.convertToWebP(data) if err != nil { diff --git a/pkg/msgconv/msgconv.go b/pkg/msgconv/msgconv.go index e4109c8..185ee0f 100644 --- a/pkg/msgconv/msgconv.go +++ b/pkg/msgconv/msgconv.go @@ -17,6 +17,9 @@ package msgconv import ( + "sync" + + "go.mau.fi/whatsmeow/types" "maunium.net/go/mautrix/bridgev2" "maunium.net/go/mautrix/format" @@ -43,12 +46,16 @@ type MessageConverter struct { DisableViewOnce bool DirectMedia bool OldMediaSuffix string + + stickerPackCache map[string]*types.StickerPack + stickerPackCacheLock sync.Mutex } func New(br *bridgev2.Bridge) *MessageConverter { mc := &MessageConverter{ - Bridge: br, - MaxFileSize: 50 * 1024 * 1024, + Bridge: br, + MaxFileSize: 50 * 1024 * 1024, + stickerPackCache: make(map[string]*types.StickerPack), } mc.HTMLParser = &format.HTMLParser{ PillConverter: mc.convertPill, diff --git a/pkg/msgconv/wa-media.go b/pkg/msgconv/wa-media.go index d1b5332..03bb274 100644 --- a/pkg/msgconv/wa-media.go +++ b/pkg/msgconv/wa-media.go @@ -35,6 +35,7 @@ import ( "maunium.net/go/mautrix/bridgev2" "maunium.net/go/mautrix/bridgev2/database" "maunium.net/go/mautrix/event" + "maunium.net/go/mautrix/id" "go.mau.fi/mautrix-whatsapp/pkg/waid" ) @@ -81,11 +82,11 @@ func (mc *MessageConverter) convertMediaMessage( MimeType: msg.GetMimetype(), } if mc.DirectMedia { - preparedMedia.FillFileName() if preparedMedia.Info.MimeType == "application/was" { preparedMedia.Info.MimeType = "video/lottie+json" preparedMedia.FileName = "sticker.json" } + preparedMedia.FillFileName() var err error portal := getPortal(ctx) idOverride := getEditTargetID(ctx) @@ -352,12 +353,15 @@ func (mc *MessageConverter) reuploadWhatsAppAttachment( ) error { client := getClient(ctx) intent := getIntent(ctx) - portal := getPortal(ctx) + var roomID id.RoomID + if portal := getPortal(ctx); portal != nil { + roomID = portal.MXID + } var thumbnailData []byte var thumbnailInfo *event.FileInfo if part.Info.Size > uploadFileThreshold { var err error - part.URL, part.File, err = intent.UploadMediaStream(ctx, portal.MXID, -1, true, func(file io.Writer) (*bridgev2.FileStreamResult, error) { + part.URL, part.File, err = intent.UploadMediaStream(ctx, roomID, -1, true, func(file io.Writer) (*bridgev2.FileStreamResult, error) { err := client.DownloadToFile(ctx, message, file.(*os.File)) if errors.Is(err, whatsmeow.ErrFileLengthMismatch) || errors.Is(err, whatsmeow.ErrInvalidMediaSHA256) { zerolog.Ctx(ctx).Warn().Err(err).Msg("Mismatching media checksums in message. Ignoring because WhatsApp seems to ignore them too") @@ -397,7 +401,7 @@ func (mc *MessageConverter) reuploadWhatsAppAttachment( part.Info.MimeType = http.DetectContentType(data) } part.FillFileName() - part.URL, part.File, err = intent.UploadMedia(ctx, portal.MXID, data, part.FileName, part.Info.MimeType) + part.URL, part.File, err = intent.UploadMedia(ctx, roomID, data, part.FileName, part.Info.MimeType) if err != nil { return fmt.Errorf("%w: %w", bridgev2.ErrMediaReuploadFailed, err) } @@ -406,7 +410,7 @@ func (mc *MessageConverter) reuploadWhatsAppAttachment( var err error part.Info.ThumbnailURL, part.Info.ThumbnailFile, err = intent.UploadMedia( ctx, - portal.MXID, + roomID, thumbnailData, "thumbnail"+exmime.ExtensionFromMimetype(thumbnailInfo.MimeType), thumbnailInfo.MimeType, diff --git a/pkg/msgconv/wa-sticker.go b/pkg/msgconv/wa-sticker.go index fef111a..85766ed 100644 --- a/pkg/msgconv/wa-sticker.go +++ b/pkg/msgconv/wa-sticker.go @@ -20,10 +20,13 @@ import ( "archive/zip" "bytes" "context" + "encoding/base64" "encoding/binary" "encoding/json" + "errors" "fmt" "io" + "net/url" "os" "path/filepath" "strconv" @@ -34,9 +37,158 @@ import ( "go.mau.fi/util/exstrings" "go.mau.fi/util/lottie" "go.mau.fi/util/random" + "go.mau.fi/whatsmeow" + "go.mau.fi/whatsmeow/types" + "maunium.net/go/mautrix" + "maunium.net/go/mautrix/bridgev2" + "maunium.net/go/mautrix/bridgev2/networkid" "maunium.net/go/mautrix/event" + + "go.mau.fi/mautrix-whatsapp/pkg/waid" ) +func (mc *MessageConverter) GetCachedStickerPack(ctx context.Context, client *whatsmeow.Client, packID string) (*types.StickerPack, error) { + mc.stickerPackCacheLock.Lock() + defer mc.stickerPackCacheLock.Unlock() + cached, ok := mc.stickerPackCache[packID] + if ok { + if cached == nil { + return nil, bridgev2.RespError(mautrix.MNotFound.WithMessage("sticker pack not found (cached)")) + } + return cached, nil + } + + pack, err := client.FetchStickerPack(ctx, packID) + if errors.Is(err, whatsmeow.ErrMediaDownloadFailedWith404) { + mc.stickerPackCache[packID] = nil + return nil, bridgev2.WrapRespErr(err, mautrix.MNotFound) + } else if err != nil { + return nil, err + } + mc.stickerPackCache[packID] = pack + if packID != pack.StickerPackID { + mc.stickerPackCache[pack.StickerPackID] = pack + } + return pack, nil +} + +func (mc *MessageConverter) GetCachedSticker(ctx context.Context, client *whatsmeow.Client, packID string, hash []byte) (*types.StickerPackItem, error) { + pack, err := mc.GetCachedStickerPack(ctx, client, packID) + if err != nil { + return nil, err + } + for _, sticker := range pack.Stickers { + if bytes.Equal(sticker.FileHash, hash) { + return sticker, nil + } + } + return nil, nil +} + +func (mc *MessageConverter) DownloadImagePack(ctx context.Context, userLoginID networkid.UserLoginID, client *whatsmeow.Client, inputURL string) (*bridgev2.ImportedImagePack, error) { + parsedURL, err := url.Parse(inputURL) + if err != nil { + return nil, bridgev2.WrapRespErr(err, mautrix.MNotFound) + } else if parsedURL.Host != "api.whatsapp.com" && parsedURL.Host != "wa.me" { + return nil, bridgev2.WrapRespErr(fmt.Errorf("invalid host %q", parsedURL.Host), mautrix.MNotFound) + } else if !strings.HasPrefix(parsedURL.Path, "/stickerpack/") { + return nil, bridgev2.WrapRespErr(fmt.Errorf("invalid path %q", parsedURL.Path), mautrix.MNotFound) + } + packName := strings.Split(strings.TrimPrefix(parsedURL.Path, "/stickerpack/"), "/")[0] + if packName == "" { + return nil, bridgev2.WrapRespErr(fmt.Errorf("empty pack name"), mautrix.MNotFound) + } + pack, err := mc.GetCachedStickerPack(ctx, client, packName) + if err != nil { + return nil, err + } + canonicalURL := "https://wa.me/stickerpack/" + pack.StickerPackID + topLevelExtra := map[string]any{ + "fi.mau.whatsapp.stickerpack": map[string]any{ + "id": pack.StickerPackID, + "name": pack.Name, + "description": pack.Description, + "publisher": pack.Publisher, + "animated": pack.Animated > 0, + "lottie": pack.Lottie > 0, + }, + } + content := &event.ImagePackEventContent{ + Images: make(map[string]*event.ImagePackImage, len(pack.Stickers)), + Metadata: event.ImagePackMetadata{ + DisplayName: pack.Name, + AvatarURL: "", + Usage: []event.ImagePackUsage{event.ImagePackUsageSticker}, + Attribution: fmt.Sprintf("By %s on WhatsApp %s", pack.Publisher, canonicalURL), + BridgedPack: &event.BridgedStickerPack{ + Network: StickerSourceID, + URL: canonicalURL, + }, + }, + } + ctx = context.WithValue(ctx, contextKeyClient, client) + ctx = context.WithValue(ctx, contextKeyIntent, mc.Bridge.Bot) + ctx = context.WithValue(ctx, contextKeyPortal, (*bridgev2.Portal)(nil)) + for i, sticker := range pack.Stickers { + shortcode := sticker.PreviewWebpID + if shortcode == "" { + shortcode = fmt.Sprintf("%s_img%d", pack.StickerPackID, i+1) + } + body := sticker.AccessibilityText + var emoji string + if len(sticker.Emojis) > 0 { + emoji = sticker.Emojis[0] + if body == "" { + body = strings.Join(sticker.Emojis, " ") + } + } + part := &PreparedMedia{ + Type: event.EventSticker, + MessageEventContent: &event.MessageEventContent{ + Body: body, + Info: &event.FileInfo{ + MimeType: sticker.MimeType, + Width: sticker.Width, + Height: sticker.Height, + Size: int(sticker.FileSize), + BridgedSticker: &event.BridgedSticker{ + Network: StickerSourceID, + ID: base64.StdEncoding.EncodeToString(sticker.FileHash), + Emoji: emoji, + PackURL: canonicalURL, + }, + }, + }, + TypeDescription: "sticker", + } + if mc.DirectMedia { + if part.Info.MimeType == "application/was" { + part.Info.MimeType = "video/lottie+json" + } + part.URL, err = mc.Bridge.Matrix.GenerateContentURI(ctx, waid.MakeStickerPackMediaID(pack.StickerPackID, sticker.FileHash, userLoginID)) + if err != nil { + panic(fmt.Errorf("failed to generate content URI: %w", err)) + } + } else { + err = mc.reuploadWhatsAppAttachment(ctx, sticker, part) + if err != nil { + return nil, fmt.Errorf("failed to reupload sticker %q: %w", sticker.GetDirectPath(), err) + } + } + content.Images[shortcode] = &event.ImagePackImage{ + URL: part.URL, + Body: part.Body, + Info: part.Info, + } + } + + return &bridgev2.ImportedImagePack{ + Content: content, + Extra: topLevelExtra, + Shortcode: pack.StickerPackID, + }, nil +} + type StickerMetadata struct { StickerPackID string `json:"sticker-pack-id"` AccessibilityText string `json:"accessibility-text"` @@ -48,7 +200,7 @@ func (sm *StickerMetadata) ToMatrix(content *event.MessageEventContent) { if sm == nil { return } - if sm.StickerPackID != "" { + if sm.StickerPackID != "" && content.Info.BridgedSticker == nil { content.Info.BridgedSticker = &event.BridgedSticker{ Network: StickerSourceID, PackURL: StickerPackURLPrefix + sm.StickerPackID, @@ -67,6 +219,24 @@ func (sm *StickerMetadata) ToMatrix(content *event.MessageEventContent) { const StickerSourceID = "whatsapp" const StickerPackURLPrefix = "https://wa.me/stickerpack/" +func PackAnimatedSticker(data []byte) ([]byte, error) { + var buf bytes.Buffer + zipWriter := zip.NewWriter(&buf) + f, err := zipWriter.Create("animation/animation.json") + if err != nil { + return nil, fmt.Errorf("failed to create zip entry: %w", err) + } + _, err = f.Write(data) + if err != nil { + return nil, fmt.Errorf("failed to write zip entry: %w", err) + } + err = zipWriter.Close() + if err != nil { + return nil, fmt.Errorf("failed to close zip writer: %w", err) + } + return buf.Bytes(), nil +} + func ExtractAnimatedSticker(data []byte) ([]byte, *StickerMetadata, error) { zipReader, err := zip.NewReader(bytes.NewReader(data), int64(len(data))) if err != nil { diff --git a/pkg/waid/mediaid.go b/pkg/waid/mediaid.go index 6a94de4..093ece1 100644 --- a/pkg/waid/mediaid.go +++ b/pkg/waid/mediaid.go @@ -33,6 +33,7 @@ const ( mediaIDTypeMessage = 255 mediaIDTypeAvatar = 254 mediaIDTypeCommunityAvatar = 253 + mediaIDTypeStickerPackItem = 252 ) func MakeMediaID(messageInfo *types.MessageInfo, idOverride types.MessageID, receiver networkid.UserLoginID) networkid.MediaID { @@ -82,9 +83,28 @@ type AvatarMediaInfo struct { Community bool } +func MakeStickerPackMediaID(packID string, fileHash []byte, receiver networkid.UserLoginID) networkid.MediaID { + receiverID := compactJID(ParseUserLoginID(receiver, 0)) + mediaID := make([]byte, 0, 4+len(packID)+len(fileHash)+len(receiverID)) + mediaID = append(mediaID, mediaIDTypeStickerPackItem) + mediaID = append(mediaID, byte(len(packID))) + mediaID = append(mediaID, packID...) + mediaID = append(mediaID, byte(len(fileHash))) + mediaID = append(mediaID, fileHash...) + mediaID = append(mediaID, byte(len(receiverID))) + mediaID = append(mediaID, receiverID...) + return mediaID +} + +type StickerPackMediaInfo struct { + PackID string + FileHash []byte +} + type ParsedMediaID struct { Message *ParsedMessageID Avatar *AvatarMediaInfo + Sticker *StickerPackMediaInfo UserLogin networkid.UserLoginID } @@ -138,6 +158,24 @@ func ParseMediaID(mediaID networkid.MediaID) (*ParsedMediaID, error) { Community: mediaIDType == mediaIDTypeCommunityAvatar, } parsed.UserLogin = MakeUserLoginID(receiverID) + case mediaIDTypeStickerPackItem: + packID, err := readCompact(&mediaID, parseString) + if err != nil { + return nil, fmt.Errorf("failed to parse sticker pack ID: %w", err) + } + fileHash, err := readCompact(&mediaID, rawBytes) + if err != nil { + return nil, fmt.Errorf("failed to parse sticker file hash: %w", err) + } + receiverID, err := readCompact(&mediaID, parseCompactJID) + if err != nil { + return nil, fmt.Errorf("failed to parse receiver JID: %w", err) + } + parsed.Sticker = &StickerPackMediaInfo{ + PackID: packID, + FileHash: fileHash, + } + parsed.UserLogin = MakeUserLoginID(receiverID) default: return nil, fmt.Errorf("unknown media ID type %d", mediaIDType) } @@ -246,6 +284,10 @@ func parseCompactJID(jid []byte) (types.JID, error) { } } +func rawBytes(data []byte) ([]byte, error) { + return data, nil +} + func readCompact[T any](data *networkid.MediaID, fn func(data []byte) (T, error)) (T, error) { var defVal T if len(*data) < 1 { From aeeea94e8ea0eb7740a470a6f5e41e51d20d27d4 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 6 May 2026 16:12:40 +0300 Subject: [PATCH 144/153] msgconv/wa-sticker: apply correct dimensions to imported packs --- pkg/msgconv/from-matrix.go | 18 +++++++++++------- pkg/msgconv/wa-media.go | 24 ++++++++++++++---------- pkg/msgconv/wa-sticker.go | 1 + 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/pkg/msgconv/from-matrix.go b/pkg/msgconv/from-matrix.go index 4eb0a88..93de90c 100644 --- a/pkg/msgconv/from-matrix.go +++ b/pkg/msgconv/from-matrix.go @@ -506,16 +506,20 @@ func (mc *MessageConverter) reuploadFileToWhatsApp( var data []byte var err error var sticker *types.StickerPackItem - if sticker, err = mc.getOriginalBridgedSticker(ctx, content.Info.BridgedSticker); sticker != nil && sticker.MimeType == "application/was" { - data, err = getClient(ctx).Download(ctx, sticker) - mime = sticker.MimeType + if sticker, err = mc.getOriginalBridgedSticker(ctx, content.Info.BridgedSticker); err != nil { + zerolog.Ctx(ctx).Warn().Err(err). + Msg("Failed to get original bridged sticker, falling back to downloading from URL") + data, err = mc.Bridge.Bot.DownloadMedia(ctx, content.URL, content.File) + } else if sticker != nil { + if sticker.MimeType == "application/was" { + data, err = getClient(ctx).Download(ctx, sticker) + mime = sticker.MimeType + } else { + data, err = mc.Bridge.Bot.DownloadMedia(ctx, content.URL, content.File) + } content.Info.Width = sticker.Width content.Info.Height = sticker.Height } else { - if err != nil { - zerolog.Ctx(ctx).Warn().Err(err). - Msg("Failed to get original bridged sticker, falling back to downloading from URL") - } data, err = mc.Bridge.Bot.DownloadMedia(ctx, content.URL, content.File) } if err != nil { diff --git a/pkg/msgconv/wa-media.go b/pkg/msgconv/wa-media.go index 03bb274..c2a8624 100644 --- a/pkg/msgconv/wa-media.go +++ b/pkg/msgconv/wa-media.go @@ -236,6 +236,19 @@ type MediaMessageWithDuration interface { const WhatsAppStickerSize = 190 +func fixStickerDimensions(info *event.FileInfo) { + if info.Width == info.Height { + info.Width = WhatsAppStickerSize + info.Height = WhatsAppStickerSize + } else if info.Width > info.Height { + info.Height /= info.Width / WhatsAppStickerSize + info.Width = WhatsAppStickerSize + } else { + info.Width /= info.Height / WhatsAppStickerSize + info.Height = WhatsAppStickerSize + } +} + func prepareMediaMessage(rawMsg MediaMessage) *PreparedMedia { extraInfo := map[string]any{} data := &PreparedMedia{ @@ -284,16 +297,7 @@ func prepareMediaMessage(rawMsg MediaMessage) *PreparedMedia { case *waE2E.StickerMessage: data.Type = event.EventSticker data.FileName = "sticker" + exmime.ExtensionFromMimetype(msg.GetMimetype()) - if data.Info.Width == data.Info.Height { - data.Info.Width = WhatsAppStickerSize - data.Info.Height = WhatsAppStickerSize - } else if data.Info.Width > data.Info.Height { - data.Info.Height /= data.Info.Width / WhatsAppStickerSize - data.Info.Width = WhatsAppStickerSize - } else { - data.Info.Width /= data.Info.Height / WhatsAppStickerSize - data.Info.Height = WhatsAppStickerSize - } + fixStickerDimensions(data.Info) case *waE2E.VideoMessage: data.MsgType = event.MsgVideo pairedMediaType := msg.GetContextInfo().GetPairedMediaType() diff --git a/pkg/msgconv/wa-sticker.go b/pkg/msgconv/wa-sticker.go index 85766ed..e9507bc 100644 --- a/pkg/msgconv/wa-sticker.go +++ b/pkg/msgconv/wa-sticker.go @@ -161,6 +161,7 @@ func (mc *MessageConverter) DownloadImagePack(ctx context.Context, userLoginID n }, TypeDescription: "sticker", } + fixStickerDimensions(part.Info) if mc.DirectMedia { if part.Info.MimeType == "application/was" { part.Info.MimeType = "video/lottie+json" From fb6ff807a865b9f853378d1b8ab86b3b02b092eb Mon Sep 17 00:00:00 2001 From: SpiritCroc Date: Thu, 7 May 2026 14:51:09 +0200 Subject: [PATCH 145/153] capabilities: promote image pack import support (#915) --- pkg/connector/capabilities.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/connector/capabilities.go b/pkg/connector/capabilities.go index d8b1367..3864356 100644 --- a/pkg/connector/capabilities.go +++ b/pkg/connector/capabilities.go @@ -19,6 +19,7 @@ var WhatsAppGeneralCaps = &bridgev2.NetworkGeneralCapabilities{ AggressiveUpdateInfo: true, ImplicitReadReceipts: true, Provisioning: bridgev2.ProvisioningCapabilities{ + ImagePackImport: true, ResolveIdentifier: bridgev2.ResolveIdentifierCapabilities{ CreateDM: true, LookupPhone: true, From 3f1223cdedd840832a5d5fbdaced4bb4198c4b9a Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 7 May 2026 17:02:00 +0300 Subject: [PATCH 146/153] dependencies: update mautrix-go --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 1f12612..b9f5347 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( golang.org/x/sync v0.20.0 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.27.1-0.20260506130904-37580129eaaf + maunium.net/go/mautrix v0.27.1-0.20260507135742-7ec18e08eac3 ) require ( diff --git a/go.sum b/go.sum index 08791dd..0575b9c 100644 --- a/go.sum +++ b/go.sum @@ -107,5 +107,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.27.1-0.20260506130904-37580129eaaf h1:AXGEYhQsiQArdtD1XE0NnGIl614j9stHXFmaB+Kb8Sw= -maunium.net/go/mautrix v0.27.1-0.20260506130904-37580129eaaf/go.mod h1:2ANjihDB+wv2UAqJapkRekmNXw7khSisccAkE5Jg3P0= +maunium.net/go/mautrix v0.27.1-0.20260507135742-7ec18e08eac3 h1:K2Aci+LppxMA2CGzj1FQBSzTh2z4F3Kv4l0tKUsaaxs= +maunium.net/go/mautrix v0.27.1-0.20260507135742-7ec18e08eac3/go.mod h1:2ANjihDB+wv2UAqJapkRekmNXw7khSisccAkE5Jg3P0= From f5f26e5ef45db052801bf72e1ff2b40ec42337c6 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 8 May 2026 17:16:10 +0300 Subject: [PATCH 147/153] msgconv/wa-sticker: cache converted sticker pack items --- pkg/msgconv/wa-sticker.go | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/pkg/msgconv/wa-sticker.go b/pkg/msgconv/wa-sticker.go index e9507bc..b9797a6 100644 --- a/pkg/msgconv/wa-sticker.go +++ b/pkg/msgconv/wa-sticker.go @@ -41,6 +41,7 @@ import ( "go.mau.fi/whatsmeow/types" "maunium.net/go/mautrix" "maunium.net/go/mautrix/bridgev2" + "maunium.net/go/mautrix/bridgev2/database" "maunium.net/go/mautrix/bridgev2/networkid" "maunium.net/go/mautrix/event" @@ -161,8 +162,11 @@ func (mc *MessageConverter) DownloadImagePack(ctx context.Context, userLoginID n }, TypeDescription: "sticker", } + dbKey := database.Key(fmt.Sprintf("stickercache:%x", part.Info.BridgedSticker.ID)) fixStickerDimensions(part.Info) + var packed *event.ImagePackImage if mc.DirectMedia { + dbKey = "" if part.Info.MimeType == "application/was" { part.Info.MimeType = "video/lottie+json" } @@ -170,17 +174,31 @@ func (mc *MessageConverter) DownloadImagePack(ctx context.Context, userLoginID n if err != nil { panic(fmt.Errorf("failed to generate content URI: %w", err)) } + } else if cached := mc.Bridge.DB.KV.Get(ctx, dbKey); cached != "" { + err = json.Unmarshal([]byte(cached), &packed) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal cached sticker data: %w", err) + } } else { err = mc.reuploadWhatsAppAttachment(ctx, sticker, part) if err != nil { return nil, fmt.Errorf("failed to reupload sticker %q: %w", sticker.GetDirectPath(), err) } } - content.Images[shortcode] = &event.ImagePackImage{ - URL: part.URL, - Body: part.Body, - Info: part.Info, + if packed == nil { + packed = &event.ImagePackImage{ + URL: part.URL, + Body: part.Body, + Info: part.Info, + } + if dbKey != "" { + data, _ := json.Marshal(packed) + if data != nil { + mc.Bridge.DB.KV.Set(ctx, dbKey, string(data)) + } + } } + content.Images[shortcode] = packed } return &bridgev2.ImportedImagePack{ From 1010a4d52060cf37734ed0658c71cac8948ef292 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 11 May 2026 14:27:06 +0300 Subject: [PATCH 148/153] dependencies: update whatsmeow --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index b9f5347..7ba785b 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/tidwall/gjson v1.18.0 go.mau.fi/util v0.9.9-0.20260505143909-8e67f0d355e0 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20260506122147-6a7198d94d26 + go.mau.fi/whatsmeow v0.0.0-20260511112314-81f8702130bd golang.org/x/image v0.39.0 golang.org/x/net v0.53.0 golang.org/x/sync v0.20.0 diff --git a/go.sum b/go.sum index 0575b9c..220edd5 100644 --- a/go.sum +++ b/go.sum @@ -75,8 +75,8 @@ go.mau.fi/util v0.9.9-0.20260505143909-8e67f0d355e0 h1:stkCMpY3ULN6sNrPoRYZ5AQ/k go.mau.fi/util v0.9.9-0.20260505143909-8e67f0d355e0/go.mod h1:jE9FfhbgEgAwxei6lomO9v8zdCIATcquONUu4vjRwSs= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20260506122147-6a7198d94d26 h1:DyFksXWn7z/NN+TNJ0DomV1/drWjkyiVuJ6RIiy/bo4= -go.mau.fi/whatsmeow v0.0.0-20260506122147-6a7198d94d26/go.mod h1:ijfkzOXauA/Vz/htXEMfOAJSUgglribW5oQeYC9tSSg= +go.mau.fi/whatsmeow v0.0.0-20260511112314-81f8702130bd h1:tqp8Bvki8H9OcoKHDmy94QiQdV7eaiSR/dD9APUlKc0= +go.mau.fi/whatsmeow v0.0.0-20260511112314-81f8702130bd/go.mod h1:ijfkzOXauA/Vz/htXEMfOAJSUgglribW5oQeYC9tSSg= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= From 4ae0bd38f2be5366a4b3004b3dede8de0e230a06 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 11 May 2026 18:57:45 +0300 Subject: [PATCH 149/153] dependencies: update whatsmeow --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 7ba785b..705e578 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/tidwall/gjson v1.18.0 go.mau.fi/util v0.9.9-0.20260505143909-8e67f0d355e0 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20260511112314-81f8702130bd + go.mau.fi/whatsmeow v0.0.0-20260511155711-eb05d94dea7d golang.org/x/image v0.39.0 golang.org/x/net v0.53.0 golang.org/x/sync v0.20.0 diff --git a/go.sum b/go.sum index 220edd5..12e29fb 100644 --- a/go.sum +++ b/go.sum @@ -75,8 +75,8 @@ go.mau.fi/util v0.9.9-0.20260505143909-8e67f0d355e0 h1:stkCMpY3ULN6sNrPoRYZ5AQ/k go.mau.fi/util v0.9.9-0.20260505143909-8e67f0d355e0/go.mod h1:jE9FfhbgEgAwxei6lomO9v8zdCIATcquONUu4vjRwSs= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20260511112314-81f8702130bd h1:tqp8Bvki8H9OcoKHDmy94QiQdV7eaiSR/dD9APUlKc0= -go.mau.fi/whatsmeow v0.0.0-20260511112314-81f8702130bd/go.mod h1:ijfkzOXauA/Vz/htXEMfOAJSUgglribW5oQeYC9tSSg= +go.mau.fi/whatsmeow v0.0.0-20260511155711-eb05d94dea7d h1:GBtuMd+MvpxZ0hII0xWzc9N4z1BPhph7aDZb6EizhO4= +go.mau.fi/whatsmeow v0.0.0-20260511155711-eb05d94dea7d/go.mod h1:ijfkzOXauA/Vz/htXEMfOAJSUgglribW5oQeYC9tSSg= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= From 1a4490843684ea488a06c487b7a04070b47b26e1 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 12 May 2026 12:24:46 +0300 Subject: [PATCH 150/153] capabilities: bump versions --- pkg/connector/capabilities.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/connector/capabilities.go b/pkg/connector/capabilities.go index 3864356..3e6f658 100644 --- a/pkg/connector/capabilities.go +++ b/pkg/connector/capabilities.go @@ -52,7 +52,7 @@ func (wa *WhatsAppConnector) GetCapabilities() *bridgev2.NetworkGeneralCapabilit } func (wa *WhatsAppConnector) GetBridgeInfoVersion() (info, caps int) { - return 1, 7 + return 1, 8 } const WAMaxFileSize = 2000 * 1024 * 1024 @@ -67,7 +67,7 @@ func supportedIfFFmpeg() event.CapabilitySupportLevel { } func capID() string { - base := "fi.mau.whatsapp.capabilities.2025_12_15" + base := "fi.mau.whatsapp.capabilities.2026_05_12" if ffmpeg.Supported() { return base + "+ffmpeg" } From 0d02df4cf5b08217945b1898da0c5a85157e4334 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 13 May 2026 15:05:53 +0300 Subject: [PATCH 151/153] dependencies: update mautrix-go --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 705e578..033ecf9 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/lib/pq v1.12.3 github.com/rs/zerolog v1.35.1 github.com/tidwall/gjson v1.18.0 - go.mau.fi/util v0.9.9-0.20260505143909-8e67f0d355e0 + go.mau.fi/util v0.9.9-0.20260511124621-9241e81bdf25 go.mau.fi/webp v0.2.0 go.mau.fi/whatsmeow v0.0.0-20260511155711-eb05d94dea7d golang.org/x/image v0.39.0 @@ -18,7 +18,7 @@ require ( golang.org/x/sync v0.20.0 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.27.1-0.20260507135742-7ec18e08eac3 + maunium.net/go/mautrix v0.27.1-0.20260513120123-5fba7e3afae4 ) require ( diff --git a/go.sum b/go.sum index 12e29fb..3ea04fb 100644 --- a/go.sum +++ b/go.sum @@ -71,8 +71,8 @@ github.com/yuin/goldmark v1.8.2 h1:kEGpgqJXdgbkhcOgBxkC0X0PmoPG1ZyoZ117rDVp4zE= github.com/yuin/goldmark v1.8.2/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= go.mau.fi/libsignal v0.2.1 h1:vRZG4EzTn70XY6Oh/pVKrQGuMHBkAWlGRC22/85m9L0= go.mau.fi/libsignal v0.2.1/go.mod h1:iVvjrHyfQqWajOUaMEsIfo3IqgVMrhWcPiiEzk7NgoU= -go.mau.fi/util v0.9.9-0.20260505143909-8e67f0d355e0 h1:stkCMpY3ULN6sNrPoRYZ5AQ/kc20a7pmhv6t0sdyVhE= -go.mau.fi/util v0.9.9-0.20260505143909-8e67f0d355e0/go.mod h1:jE9FfhbgEgAwxei6lomO9v8zdCIATcquONUu4vjRwSs= +go.mau.fi/util v0.9.9-0.20260511124621-9241e81bdf25 h1:YPEmc+li7TF6C9AdRTcSLMb6yCHdF27/wNT7kFLIVNg= +go.mau.fi/util v0.9.9-0.20260511124621-9241e81bdf25/go.mod h1:jE9FfhbgEgAwxei6lomO9v8zdCIATcquONUu4vjRwSs= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= go.mau.fi/whatsmeow v0.0.0-20260511155711-eb05d94dea7d h1:GBtuMd+MvpxZ0hII0xWzc9N4z1BPhph7aDZb6EizhO4= @@ -107,5 +107,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.27.1-0.20260507135742-7ec18e08eac3 h1:K2Aci+LppxMA2CGzj1FQBSzTh2z4F3Kv4l0tKUsaaxs= -maunium.net/go/mautrix v0.27.1-0.20260507135742-7ec18e08eac3/go.mod h1:2ANjihDB+wv2UAqJapkRekmNXw7khSisccAkE5Jg3P0= +maunium.net/go/mautrix v0.27.1-0.20260513120123-5fba7e3afae4 h1:zNC9eVAhw8FhKpM3AxNAh/iy75UEYX91uJUvqqAYlvo= +maunium.net/go/mautrix v0.27.1-0.20260513120123-5fba7e3afae4/go.mod h1:3sOGhXi3P1V6/NruTA0gujkvTypXVUraWktCuTGyDuM= From bf12524eefee5c83ca2eae8c759715138e0efd09 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 13 May 2026 17:03:48 +0300 Subject: [PATCH 152/153] handlewhatsapp: decrypt message secret data before rerouting LIDs --- go.mod | 2 +- go.sum | 4 +- pkg/connector/handlewhatsapp.go | 74 +++++++++++++++++---------------- 3 files changed, 42 insertions(+), 38 deletions(-) diff --git a/go.mod b/go.mod index 033ecf9..9cd6c8f 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/tidwall/gjson v1.18.0 go.mau.fi/util v0.9.9-0.20260511124621-9241e81bdf25 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20260511155711-eb05d94dea7d + go.mau.fi/whatsmeow v0.0.0-20260513140310-c551a4055c0f golang.org/x/image v0.39.0 golang.org/x/net v0.53.0 golang.org/x/sync v0.20.0 diff --git a/go.sum b/go.sum index 3ea04fb..be22fdf 100644 --- a/go.sum +++ b/go.sum @@ -75,8 +75,8 @@ go.mau.fi/util v0.9.9-0.20260511124621-9241e81bdf25 h1:YPEmc+li7TF6C9AdRTcSLMb6y go.mau.fi/util v0.9.9-0.20260511124621-9241e81bdf25/go.mod h1:jE9FfhbgEgAwxei6lomO9v8zdCIATcquONUu4vjRwSs= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= -go.mau.fi/whatsmeow v0.0.0-20260511155711-eb05d94dea7d h1:GBtuMd+MvpxZ0hII0xWzc9N4z1BPhph7aDZb6EizhO4= -go.mau.fi/whatsmeow v0.0.0-20260511155711-eb05d94dea7d/go.mod h1:ijfkzOXauA/Vz/htXEMfOAJSUgglribW5oQeYC9tSSg= +go.mau.fi/whatsmeow v0.0.0-20260513140310-c551a4055c0f h1:icWtsD1MH5nlo8mEpHMPZ9+1kgHkjmXQroYi0lHXKZ0= +go.mau.fi/whatsmeow v0.0.0-20260513140310-c551a4055c0f/go.mod h1:ijfkzOXauA/Vz/htXEMfOAJSUgglribW5oQeYC9tSSg= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index eb42f43..4c1016c 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -309,14 +309,50 @@ func (wa *WhatsAppClient) rerouteWAMessage(ctx context.Context, evtType string, func (wa *WhatsAppClient) handleWAMessage(ctx context.Context, evt *events.Message) (success bool) { success = true + if evt.Info.Chat == types.StatusBroadcastJID && !wa.Main.Config.EnableStatusBroadcast { + return + } + parsedMessageType := getMessageType(evt.Message) + if parsedMessageType == "ignore" || strings.HasPrefix(parsedMessageType, "unknown_protocol_") { + return + } + if encReact := evt.Message.GetEncReactionMessage(); encReact != nil { + decrypted, err := wa.Client.DecryptReaction(ctx, evt) + if err != nil { + wa.UserLogin.Log.Err(err).Str("message_id", evt.Info.ID).Msg("Failed to decrypt reaction") + return + } + decrypted.Key = encReact.GetTargetMessageKey() + evt.Message.ReactionMessage = decrypted + } + if encComment := evt.Message.GetEncCommentMessage(); encComment != nil { + decrypted, err := wa.Client.DecryptComment(ctx, evt) + if err != nil { + wa.UserLogin.Log.Err(err).Str("message_id", evt.Info.ID).Msg("Failed to decrypt comment") + } else { + decrypted.EncCommentMessage = evt.Message.GetEncCommentMessage() + evt.Message = decrypted + } + } + if encMessage := evt.Message.GetSecretEncryptedMessage(); encMessage != nil { + decrypted, err := wa.Client.DecryptSecretEncryptedMessage(ctx, evt) + if err != nil { + wa.UserLogin.Log.Err(err). + Str("message_id", evt.Info.ID). + Stringer("evt_sender", evt.Info.Sender). + Any("target_message_key", encMessage.TargetMessageKey). + Msg("Failed to decrypt secret-encrypted message") + return + } + evt.RawMessage = decrypted + evt.UnwrapRaw() + parsedMessageType = getMessageType(evt.Message) + } wa.rerouteWAMessage(ctx, "message", &evt.Info.MessageSource, evt.Info.ID) wa.UserLogin.Log.Trace(). Any("info", evt.Info). Any("payload", evt.Message). Msg("Received WhatsApp message") - if evt.Info.Chat == types.StatusBroadcastJID && !wa.Main.Config.EnableStatusBroadcast { - return - } if evt.Info.IsFromMe && evt.Message.GetProtocolMessage().GetHistorySyncNotification() != nil && wa.Main.Bridge.Config.Backfill.Enabled && @@ -351,38 +387,6 @@ func (wa *WhatsAppClient) handleWAMessage(ctx context.Context, evt *events.Messa return } - parsedMessageType := getMessageType(evt.Message) - if parsedMessageType == "ignore" || strings.HasPrefix(parsedMessageType, "unknown_protocol_") { - return - } - if encReact := evt.Message.GetEncReactionMessage(); encReact != nil { - decrypted, err := wa.Client.DecryptReaction(ctx, evt) - if err != nil { - wa.UserLogin.Log.Err(err).Str("message_id", evt.Info.ID).Msg("Failed to decrypt reaction") - return - } - decrypted.Key = encReact.GetTargetMessageKey() - evt.Message.ReactionMessage = decrypted - } - if encComment := evt.Message.GetEncCommentMessage(); encComment != nil { - decrypted, err := wa.Client.DecryptComment(ctx, evt) - if err != nil { - wa.UserLogin.Log.Err(err).Str("message_id", evt.Info.ID).Msg("Failed to decrypt comment") - } else { - decrypted.EncCommentMessage = evt.Message.GetEncCommentMessage() - evt.Message = decrypted - } - } - if encMessage := evt.Message.GetSecretEncryptedMessage(); encMessage != nil { - decrypted, err := wa.Client.DecryptSecretEncryptedMessage(ctx, evt) - if err != nil { - wa.UserLogin.Log.Err(err).Str("message_id", evt.Info.ID).Msg("Failed to decrypt message") - return - } - evt.RawMessage = decrypted - evt.UnwrapRaw() - parsedMessageType = getMessageType(evt.Message) - } res := wa.UserLogin.QueueRemoteEvent(&WAMessageEvent{ MessageInfoWrapper: &MessageInfoWrapper{ Info: evt.Info, From 7f91a71e9d058d2a0cf86e94260b2a117ddbdd65 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 14 May 2026 15:27:58 +0300 Subject: [PATCH 153/153] handlewhatsapp: fix saving history sync notifications --- pkg/connector/handlewhatsapp.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index 4c1016c..3abcede 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -313,9 +313,6 @@ func (wa *WhatsAppClient) handleWAMessage(ctx context.Context, evt *events.Messa return } parsedMessageType := getMessageType(evt.Message) - if parsedMessageType == "ignore" || strings.HasPrefix(parsedMessageType, "unknown_protocol_") { - return - } if encReact := evt.Message.GetEncReactionMessage(); encReact != nil { decrypted, err := wa.Client.DecryptReaction(ctx, evt) if err != nil { @@ -355,10 +352,12 @@ func (wa *WhatsAppClient) handleWAMessage(ctx context.Context, evt *events.Messa Msg("Received WhatsApp message") if evt.Info.IsFromMe && evt.Message.GetProtocolMessage().GetHistorySyncNotification() != nil && - wa.Main.Bridge.Config.Backfill.Enabled && - wa.Client.ManualHistorySyncDownload { + wa.Main.Bridge.Config.Backfill.Enabled { wa.saveWAHistorySyncNotification(ctx, evt.Message.ProtocolMessage.HistorySyncNotification) } + if parsedMessageType == "ignore" || strings.HasPrefix(parsedMessageType, "unknown_protocol_") { + return + } messageAssoc := evt.Message.GetMessageContextInfo().GetMessageAssociation() if assocType := messageAssoc.GetAssociationType(); assocType == waE2E.MessageAssociation_HD_IMAGE_DUAL_UPLOAD || assocType == waE2E.MessageAssociation_HD_VIDEO_DUAL_UPLOAD {