From 44611aa85092bd7d566935630da14f7e6c5dfba3 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 5 May 2025 23:39:10 +0300 Subject: [PATCH 001/276] dependencies: update mautrix-go --- go.mod | 2 +- go.sum | 4 ++-- pkg/connector/connector.go | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 718687c..071d58d 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( golang.org/x/sync v0.13.0 google.golang.org/protobuf v1.36.6 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.23.4-0.20250501132821-5094eea71864 + maunium.net/go/mautrix v0.23.4-0.20250505203826-970ea996a2f4 ) require ( diff --git a/go.sum b/go.sum index 5070d5b..cc37ba5 100644 --- a/go.sum +++ b/go.sum @@ -101,5 +101,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.23.4-0.20250501132821-5094eea71864 h1:1NmHnfbxtVlX2hcwL+zTEnVKSBncZIFV6NW9i2q8EaI= -maunium.net/go/mautrix v0.23.4-0.20250501132821-5094eea71864/go.mod h1:pT4G5RZQ+nLfKzsmeDa4NhHghOVTrasLLwY9tZ2mO08= +maunium.net/go/mautrix v0.23.4-0.20250505203826-970ea996a2f4 h1:cia1//Az4ApDJVg15RxVX6j5LRs7ap3lGbD3IltEGyQ= +maunium.net/go/mautrix v0.23.4-0.20250505203826-970ea996a2f4/go.mod h1:pT4G5RZQ+nLfKzsmeDa4NhHghOVTrasLLwY9tZ2mO08= diff --git a/pkg/connector/connector.go b/pkg/connector/connector.go index d052ed2..72c2cbc 100644 --- a/pkg/connector/connector.go +++ b/pkg/connector/connector.go @@ -61,6 +61,8 @@ var ( _ bridgev2.NetworkConnector = (*WhatsAppConnector)(nil) _ bridgev2.MaxFileSizeingNetwork = (*WhatsAppConnector)(nil) _ bridgev2.StoppableNetwork = (*WhatsAppConnector)(nil) + + _ bridgev2.TransactionIDGeneratingNetwork = (*WhatsAppConnector)(nil) ) func (wa *WhatsAppConnector) SetMaxFileSize(maxSize int64) { From 6f3e8ea5d1f706d6cb859f1c6faab20cf0f727cc Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 14 May 2025 14:40:03 +0300 Subject: [PATCH 002/276] all: update whatsmeow to use more contexts --- cmd/mautrix-whatsapp/legacyprovision.go | 2 +- go.mod | 18 +++++++------- go.sum | 32 ++++++++++++------------- pkg/connector/chatinfo.go | 8 +++---- pkg/connector/client.go | 7 +++--- pkg/connector/commands.go | 2 +- pkg/connector/connector.go | 4 ++-- pkg/connector/directmedia.go | 4 ++-- pkg/connector/handlematrix.go | 2 +- pkg/connector/handlewhatsapp.go | 11 +++++---- pkg/connector/login.go | 2 +- pkg/connector/startchat.go | 2 +- pkg/connector/userinfo.go | 8 +++---- pkg/msgconv/from-matrix.go | 2 +- pkg/msgconv/matrixpoll.go | 2 +- pkg/msgconv/urlpreview.go | 2 +- pkg/msgconv/wa-media.go | 4 ++-- pkg/msgconv/wa-poll.go | 2 +- 18 files changed, 58 insertions(+), 56 deletions(-) diff --git a/cmd/mautrix-whatsapp/legacyprovision.go b/cmd/mautrix-whatsapp/legacyprovision.go index 30e43c5..4b4793c 100644 --- a/cmd/mautrix-whatsapp/legacyprovision.go +++ b/cmd/mautrix-whatsapp/legacyprovision.go @@ -236,7 +236,7 @@ func legacyProvContacts(w http.ResponseWriter, r *http.Request) { if userLogin == nil { return } - if contacts, err := userLogin.Client.(*connector.WhatsAppClient).Device.Contacts.GetAllContacts(); err != nil { + if contacts, err := userLogin.Client.(*connector.WhatsAppClient).Device.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/go.mod b/go.mod index 718687c..62d2d7f 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module go.mau.fi/mautrix-whatsapp go 1.23.0 -toolchain go1.24.2 +toolchain go1.24.3 require ( github.com/gorilla/mux v1.8.0 @@ -11,13 +11,13 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.8.7-0.20250427215252-d2d18a7e463c go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250501130609-4c93ee4e6efa + go.mau.fi/whatsmeow v0.0.0-20250514120219-ef9c1e91094e golang.org/x/image v0.26.0 - golang.org/x/net v0.39.0 - golang.org/x/sync v0.13.0 + golang.org/x/net v0.40.0 + golang.org/x/sync v0.14.0 google.golang.org/protobuf v1.36.6 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.23.4-0.20250501132821-5094eea71864 + maunium.net/go/mautrix v0.23.4-0.20250509121614-a0191c8f5847 ) require ( @@ -37,12 +37,12 @@ require ( github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/sjson v1.2.5 // indirect github.com/yuin/goldmark v1.7.10 // indirect - go.mau.fi/libsignal v0.1.2 // indirect + go.mau.fi/libsignal v0.2.0 // indirect go.mau.fi/zeroconfig v0.1.3 // indirect - golang.org/x/crypto v0.37.0 // indirect + golang.org/x/crypto v0.38.0 // indirect golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect - golang.org/x/sys v0.32.0 // indirect - golang.org/x/text v0.24.0 // indirect + golang.org/x/sys v0.33.0 // indirect + golang.org/x/text v0.25.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 5070d5b..4317b48 100644 --- a/go.sum +++ b/go.sum @@ -63,33 +63,33 @@ github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= github.com/yuin/goldmark v1.7.10 h1:S+LrtBjRmqMac2UdtB6yyCEJm+UILZ2fefI4p7o0QpI= github.com/yuin/goldmark v1.7.10/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= -go.mau.fi/libsignal v0.1.2 h1:Vs16DXWxSKyzVtI+EEXLCSy5pVWzzCzp/2eqFGvLyP0= -go.mau.fi/libsignal v0.1.2/go.mod h1:JpnLSSJptn/s1sv7I56uEMywvz8x4YzxeF5OzdPb6PE= +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.8.7-0.20250427215252-d2d18a7e463c h1:qfJyMZq1pPyuXKoVWwHs6OmR9CzO3pHFRPYT/QpaaaA= go.mau.fi/util v0.8.7-0.20250427215252-d2d18a7e463c/go.mod h1:uNB3UTXFbkpp7xL1M/WvQks90B/L4gvbLpbS0603KOE= 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-20250501130609-4c93ee4e6efa h1:+bQKfMtnhX2jVoCSaneH4Ctk51IVT1K2gvjyqfFjVW0= -go.mau.fi/whatsmeow v0.0.0-20250501130609-4c93ee4e6efa/go.mod h1:NlPtoLdpX3RnltqCTCZQ6kIUfprqLirtSK1gHvwoNx0= +go.mau.fi/whatsmeow v0.0.0-20250514120219-ef9c1e91094e h1:7lboFyrizLaof8CBFIlqk1B50DVeVAq0vlMkBQVGE7A= +go.mau.fi/whatsmeow v0.0.0-20250514120219-ef9c1e91094e/go.mod h1:0b/hOeTpA38V89YTtE2yqMKfDfaffj8gMu+AswqprQI= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= -golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= -golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= +golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= +golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM= golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8= golang.org/x/image v0.26.0 h1:4XjIFEZWQmCZi6Wv8BoxsDhRU3RVnLX04dToTDAEPlY= golang.org/x/image v0.26.0/go.mod h1:lcxbMFAovzpnJxzXS3nyL83K27tmqtKzIJpctK8YO5c= -golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= -golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= -golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= -golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= +golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= +golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= +golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 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.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= -golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= -golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= +golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -101,5 +101,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.23.4-0.20250501132821-5094eea71864 h1:1NmHnfbxtVlX2hcwL+zTEnVKSBncZIFV6NW9i2q8EaI= -maunium.net/go/mautrix v0.23.4-0.20250501132821-5094eea71864/go.mod h1:pT4G5RZQ+nLfKzsmeDa4NhHghOVTrasLLwY9tZ2mO08= +maunium.net/go/mautrix v0.23.4-0.20250509121614-a0191c8f5847 h1:umv6WzuHjAVLVJlNeGEUogZtcD+3I6/OIpFnThUBb+A= +maunium.net/go/mautrix v0.23.4-0.20250509121614-a0191c8f5847/go.mod h1:pT4G5RZQ+nLfKzsmeDa4NhHghOVTrasLLwY9tZ2mO08= diff --git a/pkg/connector/chatinfo.go b/pkg/connector/chatinfo.go index 81776dc..af75af3 100644 --- a/pkg/connector/chatinfo.go +++ b/pkg/connector/chatinfo.go @@ -92,7 +92,7 @@ func updateDisappearingTimerSetAt(ts int64) bridgev2.ExtraUpdater[*bridgev2.Port } func (wa *WhatsAppClient) applyChatSettings(ctx context.Context, chatID types.JID, info *bridgev2.ChatInfo) { - chat, err := wa.GetStore().ChatSettings.GetChatSettings(chatID) + chat, err := wa.GetStore().ChatSettings.GetChatSettings(ctx, chatID) if err != nil { zerolog.Ctx(ctx).Warn().Err(err).Msg("Failed to get chat settings") return @@ -406,7 +406,7 @@ func (wa *WhatsAppClient) makePortalAvatarFetcher(avatarID string, sender types. wrappedAvatar = &bridgev2.Avatar{ ID: networkid.AvatarID(avatar.ID), Get: func(ctx context.Context) ([]byte, error) { - return wa.Client.DownloadMediaWithPath(avatar.DirectPath, nil, nil, nil, 0, "", "") + return wa.Client.DownloadMediaWithPath(ctx, avatar.DirectPath, nil, nil, nil, 0, "", "") }, } } @@ -441,7 +441,7 @@ func (wa *WhatsAppClient) wrapNewsletterInfo(info *types.NewsletterMetadata) *br if info.ThreadMeta.Picture != nil { avatar.ID = networkid.AvatarID(info.ThreadMeta.Picture.ID) avatar.Get = func(ctx context.Context) ([]byte, error) { - return wa.Client.DownloadMediaWithPath(info.ThreadMeta.Picture.DirectPath, nil, nil, nil, 0, "", "") + return wa.Client.DownloadMediaWithPath(ctx, info.ThreadMeta.Picture.DirectPath, nil, nil, nil, 0, "", "") } } else if info.ThreadMeta.Preview.ID != "" { avatar.ID = networkid.AvatarID(info.ThreadMeta.Preview.ID) @@ -452,7 +452,7 @@ func (wa *WhatsAppClient) wrapNewsletterInfo(info *types.NewsletterMetadata) *br } else if meta.ThreadMeta.Picture == nil { return nil, fmt.Errorf("full res avatar info is missing") } - return wa.Client.DownloadMediaWithPath(meta.ThreadMeta.Picture.DirectPath, nil, nil, nil, 0, "", "") + return wa.Client.DownloadMediaWithPath(ctx, meta.ThreadMeta.Picture.DirectPath, nil, nil, nil, 0, "", "") } } else { avatar.ID = "remove" diff --git a/pkg/connector/client.go b/pkg/connector/client.go index d9a752f..b6eb376 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -41,7 +41,7 @@ import ( "go.mau.fi/mautrix-whatsapp/pkg/waid" ) -func (wa *WhatsAppConnector) LoadUserLogin(_ context.Context, login *bridgev2.UserLogin) error { +func (wa *WhatsAppConnector) LoadUserLogin(ctx context.Context, login *bridgev2.UserLogin) error { w := &WhatsAppClient{ Main: wa, UserLogin: login, @@ -60,7 +60,7 @@ func (wa *WhatsAppConnector) LoadUserLogin(_ context.Context, login *bridgev2.Us var err error w.JID = waid.ParseUserLoginID(login.ID, loginMetadata.WADeviceID) - w.Device, err = wa.DeviceStore.GetDevice(w.JID) + w.Device, err = wa.DeviceStore.GetDevice(ctx, w.JID) if err != nil { return err } @@ -71,6 +71,7 @@ func (wa *WhatsAppConnector) LoadUserLogin(_ context.Context, login *bridgev2.Us w.Client.AddEventHandler(w.handleWAEvent) if bridgev2.PortalEventBuffer == 0 { w.Client.SynchronousAck = true + w.Client.EnableDecryptedEventBuffer = true } w.Client.AutomaticMessageRerequestFromPhone = true w.Client.GetMessageForRetry = w.trackNotFoundRetry @@ -324,7 +325,7 @@ func (wa *WhatsAppClient) Disconnect() { func (wa *WhatsAppClient) LogoutRemote(ctx context.Context) { if cli := wa.Client; cli != nil { - err := cli.Logout() + err := cli.Logout(ctx) if err != nil { zerolog.Ctx(ctx).Err(err).Msg("Failed to log out") } diff --git a/pkg/connector/commands.go b/pkg/connector/commands.go index 4c62db0..8e2ddf0 100644 --- a/pkg/connector/commands.go +++ b/pkg/connector/commands.go @@ -142,7 +142,7 @@ func fnSync(ce *commands.Event) { ce.React("✅") case "appstate": for _, name := range appstate.AllPatchNames { - err := wa.Client.FetchAppState(name, true, false) + 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) return diff --git a/pkg/connector/connector.go b/pkg/connector/connector.go index d052ed2..ca8cd5b 100644 --- a/pkg/connector/connector.go +++ b/pkg/connector/connector.go @@ -127,7 +127,7 @@ func (wa *WhatsAppConnector) Init(bridge *bridgev2.Bridge) { } func (wa *WhatsAppConnector) Start(ctx context.Context) error { - err := wa.DeviceStore.Upgrade() + err := wa.DeviceStore.Upgrade(ctx) if err != nil { return bridgev2.DBUpgradeError{Err: err, Section: "whatsmeow"} } @@ -152,7 +152,7 @@ func (wa *WhatsAppConnector) Stop() { } func (wa *WhatsAppConnector) onFirstClientConnect() { - ver, err := whatsmeow.GetLatestVersion(nil) + ver, err := whatsmeow.GetLatestVersion(wa.Bridge.BackgroundCtx, nil) if err != nil { wa.Bridge.Log.Err(err).Msg("Failed to get latest WhatsApp web version number") } else { diff --git a/pkg/connector/directmedia.go b/pkg/connector/directmedia.go index 64bb5e4..df234ff 100644 --- a/pkg/connector/directmedia.go +++ b/pkg/connector/directmedia.go @@ -99,7 +99,7 @@ func (wa *WhatsAppConnector) Download(ctx context.Context, mediaID networkid.Med } return &mediaproxy.GetMediaResponseFile{ Callback: func(f *os.File) error { - err := waClient.Client.DownloadToFile(keys, f) + err := waClient.Client.DownloadToFile(ctx, keys, f) if errors.Is(err, whatsmeow.ErrMediaDownloadFailedWith403) || errors.Is(err, whatsmeow.ErrMediaDownloadFailedWith404) || errors.Is(err, whatsmeow.ErrMediaDownloadFailedWith410) { val := params["fi.mau.whatsapp.reload_media"] if val == "false" || (!wa.Config.DirectMediaAutoRequest && val != "true") { @@ -112,7 +112,7 @@ func (wa *WhatsAppConnector) Download(ctx context.Context, mediaID networkid.Med return err } log.Trace().Msg("Retrying download after successful retry") - err = waClient.Client.DownloadToFile(keys, f) + err = waClient.Client.DownloadToFile(ctx, keys, f) } 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") diff --git a/pkg/connector/handlematrix.go b/pkg/connector/handlematrix.go index 8dfa5c9..a90b677 100644 --- a/pkg/connector/handlematrix.go +++ b/pkg/connector/handlematrix.go @@ -156,7 +156,7 @@ func (wa *WhatsAppClient) HandleMatrixReaction(ctx context.Context, msg *bridgev } var req whatsmeow.SendRequestExtra if msg.Portal.Metadata.(*waid.PortalMetadata).CommunityAnnouncementGroup { - reactionMsg.EncReactionMessage, err = wa.Client.EncryptReaction(msgconv.MessageIDToInfo(wa.Client, messageID), reactionMsg.ReactionMessage) + reactionMsg.EncReactionMessage, err = wa.Client.EncryptReaction(ctx, msgconv.MessageIDToInfo(wa.Client, messageID), reactionMsg.ReactionMessage) if err != nil { return nil, fmt.Errorf("failed to encrypt reaction: %w", err) } diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index 0a83902..196905e 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -73,10 +73,11 @@ func init() { func (wa *WhatsAppClient) handleWAEvent(rawEvt any) { log := wa.UserLogin.Log + ctx := log.WithContext(wa.Main.Bridge.BackgroundCtx) switch evt := rawEvt.(type) { case *events.Message: - wa.handleWAMessage(evt) + wa.handleWAMessage(ctx, evt) case *events.Receipt: wa.handleWAReceipt(evt) case *events.ChatPresence: @@ -143,7 +144,7 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) { if err != nil { log.Warn().Err(err).Msg("Failed to send presence after push name update") } - _, _, err = wa.GetStore().Contacts.PutPushName(wa.JID.ToNonAD(), evt.Action.GetName()) + _, _, err = wa.GetStore().Contacts.PutPushName(ctx, wa.JID.ToNonAD(), evt.Action.GetName()) if err != nil { log.Err(err).Msg("Failed to update push name in store") } @@ -254,7 +255,7 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) { } } -func (wa *WhatsAppClient) handleWAMessage(evt *events.Message) { +func (wa *WhatsAppClient) handleWAMessage(ctx context.Context, evt *events.Message) { wa.UserLogin.Log.Trace(). Any("info", evt.Info). Any("payload", evt.Message). @@ -267,7 +268,7 @@ func (wa *WhatsAppClient) handleWAMessage(evt *events.Message) { return } if encReact := evt.Message.GetEncReactionMessage(); encReact != nil { - decrypted, err := wa.Client.DecryptReaction(evt) + 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 @@ -276,7 +277,7 @@ func (wa *WhatsAppClient) handleWAMessage(evt *events.Message) { evt.Message.ReactionMessage = decrypted } if encComment := evt.Message.GetEncCommentMessage(); encComment != nil { - decrypted, err := wa.Client.DecryptComment(evt) + 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 { diff --git a/pkg/connector/login.go b/pkg/connector/login.go index cc04b6f..7a232d9 100644 --- a/pkg/connector/login.go +++ b/pkg/connector/login.go @@ -162,7 +162,7 @@ func (wl *WALogin) SubmitUserInput(ctx context.Context, input map[string]string) wl.Log.Warn().Err(err).Msg("Timed out waiting for connection") return nil, fmt.Errorf("failed to wait for connection: %w", err) } - pairingCode, err := wl.Client.PairPhone(input["phone_number"], true, whatsmeow.PairClientChrome, "Chrome (Linux)") + pairingCode, err := wl.Client.PairPhone(ctx, input["phone_number"], true, whatsmeow.PairClientChrome, "Chrome (Linux)") if err != nil { wl.Log.Err(err).Msg("Failed to request phone code login") return nil, err diff --git a/pkg/connector/startchat.go b/pkg/connector/startchat.go index 4f4ce64..e2631bc 100644 --- a/pkg/connector/startchat.go +++ b/pkg/connector/startchat.go @@ -136,7 +136,7 @@ func (wa *WhatsAppClient) getContactList(ctx context.Context, filter string) ([] if !wa.IsLoggedIn() { return nil, mautrix.MForbidden.WithMessage("You must be logged in to list contacts") } - contacts, err := wa.GetStore().Contacts.GetAllContacts() + contacts, err := wa.GetStore().Contacts.GetAllContacts(ctx) if err != nil { return nil, err } diff --git a/pkg/connector/userinfo.go b/pkg/connector/userinfo.go index f4e0d33..c0520b7 100644 --- a/pkg/connector/userinfo.go +++ b/pkg/connector/userinfo.go @@ -169,7 +169,7 @@ func (wa *WhatsAppClient) GetUserInfo(ctx context.Context, ghost *bridgev2.Ghost } func (wa *WhatsAppClient) getUserInfo(ctx context.Context, jid types.JID, fetchAvatar bool) (*bridgev2.UserInfo, error) { - contact, err := wa.GetStore().Contacts.GetContact(jid) + contact, err := wa.GetStore().Contacts.GetContact(ctx, jid) if err != nil { return nil, err } @@ -191,7 +191,7 @@ func (wa *WhatsAppClient) contactToUserInfo(ctx context.Context, jid types.JID, zerolog.Ctx(ctx).Err(err).Stringer("lid", jid).Msg("Failed to get PN for LID") } else { phone = "+" + pnJID.User - extraContact, err := wa.GetStore().Contacts.GetContact(pnJID) + extraContact, err := wa.GetStore().Contacts.GetContact(ctx, pnJID) if err != nil { zerolog.Ctx(ctx).Err(err). Stringer("lid", jid). @@ -262,7 +262,7 @@ func (wa *WhatsAppClient) fetchGhostAvatar(ctx context.Context, ghost *bridgev2. wrappedAvatar = &bridgev2.Avatar{ ID: networkid.AvatarID(avatar.ID), Get: func(ctx context.Context) ([]byte, error) { - return wa.Client.DownloadMediaWithPath(avatar.DirectPath, nil, nil, nil, 0, "", "") + return wa.Client.DownloadMediaWithPath(ctx, avatar.DirectPath, nil, nil, nil, 0, "", "") }, } } @@ -272,7 +272,7 @@ func (wa *WhatsAppClient) fetchGhostAvatar(ctx context.Context, ghost *bridgev2. func (wa *WhatsAppClient) resyncContacts(forceAvatarSync bool) { log := wa.UserLogin.Log.With().Str("action", "resync contacts").Logger() ctx := log.WithContext(context.Background()) - contacts, err := wa.GetStore().Contacts.GetAllContacts() + contacts, err := wa.GetStore().Contacts.GetAllContacts(ctx) if err != nil { log.Err(err).Msg("Failed to get cached contacts") return diff --git a/pkg/msgconv/from-matrix.go b/pkg/msgconv/from-matrix.go index 9b9b529..3e82b06 100644 --- a/pkg/msgconv/from-matrix.go +++ b/pkg/msgconv/from-matrix.go @@ -122,7 +122,7 @@ func (mc *MessageConverter) ToWhatsApp( return nil, nil, fmt.Errorf("failed to parse message ID: %w", err) } rootMsgInfo := MessageIDToInfo(client, parsedID) - message, err = client.EncryptComment(rootMsgInfo, message) + message, err = client.EncryptComment(ctx, rootMsgInfo, message) if err != nil { return nil, nil, fmt.Errorf("failed to encrypt comment: %w", err) } diff --git a/pkg/msgconv/matrixpoll.go b/pkg/msgconv/matrixpoll.go index b4b20b1..34040ab 100644 --- a/pkg/msgconv/matrixpoll.go +++ b/pkg/msgconv/matrixpoll.go @@ -140,7 +140,7 @@ func (mc *MessageConverter) PollVoteToWhatsApp( } } } - pollUpdate, err := client.EncryptPollVote(pollMsgInfo, &waE2E.PollVoteMessage{ + pollUpdate, err := client.EncryptPollVote(ctx, pollMsgInfo, &waE2E.PollVoteMessage{ SelectedOptions: optionHashes, }) return &waE2E.Message{PollUpdateMessage: pollUpdate}, err diff --git a/pkg/msgconv/urlpreview.go b/pkg/msgconv/urlpreview.go index 62ac312..cb241c7 100644 --- a/pkg/msgconv/urlpreview.go +++ b/pkg/msgconv/urlpreview.go @@ -51,7 +51,7 @@ func (mc *MessageConverter) convertURLPreviewToBeeper(ctx context.Context, msg * var thumbnailData []byte if msg.ThumbnailDirectPath != nil { var err error - thumbnailData, err = getClient(ctx).DownloadThumbnail(msg) + thumbnailData, err = getClient(ctx).DownloadThumbnail(ctx, msg) if err != nil { zerolog.Ctx(ctx).Err(err).Msg("Failed to download thumbnail for link preview") } diff --git a/pkg/msgconv/wa-media.go b/pkg/msgconv/wa-media.go index dea7f54..d5f3be0 100644 --- a/pkg/msgconv/wa-media.go +++ b/pkg/msgconv/wa-media.go @@ -318,7 +318,7 @@ func (mc *MessageConverter) reuploadWhatsAppAttachment( 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) { - err := client.DownloadToFile(message, file.(*os.File)) + 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") } else if err != nil { @@ -339,7 +339,7 @@ func (mc *MessageConverter) reuploadWhatsAppAttachment( return err } } else { - data, err := client.Download(message) + data, err := client.Download(ctx, message) 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 { diff --git a/pkg/msgconv/wa-poll.go b/pkg/msgconv/wa-poll.go index 1087684..97a75e2 100644 --- a/pkg/msgconv/wa-poll.go +++ b/pkg/msgconv/wa-poll.go @@ -144,7 +144,7 @@ func (mc *MessageConverter) convertPollUpdateMessage(ctx context.Context, info * log.Err(err).Msg("Failed to get poll update target message") return failedPollUpdatePart, nil } - vote, err := getClient(ctx).DecryptPollVote(&events.Message{ + vote, err := getClient(ctx).DecryptPollVote(ctx, &events.Message{ Info: *info, Message: &waE2E.Message{PollUpdateMessage: msg}, }) From 81f5f3b5222139bcdf11acf38e8b15daa056bd94 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 14 May 2025 15:07:50 +0300 Subject: [PATCH 003/276] dependencies: update --- go.mod | 12 ++++++------ go.sum | 24 ++++++++++++------------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index 62d2d7f..94a3145 100644 --- a/go.mod +++ b/go.mod @@ -9,10 +9,10 @@ require ( github.com/gorilla/websocket v1.5.0 github.com/lib/pq v1.10.9 github.com/rs/zerolog v1.34.0 - go.mau.fi/util v0.8.7-0.20250427215252-d2d18a7e463c + go.mau.fi/util v0.8.7-0.20250514115735-05fea4054bbd go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250514120219-ef9c1e91094e - golang.org/x/image v0.26.0 + go.mau.fi/whatsmeow v0.0.0-20250514120708-22ca98ea604a + golang.org/x/image v0.27.0 golang.org/x/net v0.40.0 golang.org/x/sync v0.14.0 google.golang.org/protobuf v1.36.6 @@ -27,8 +27,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.27 // indirect - github.com/petermattis/goid v0.0.0-20250319124200-ccd6737f222a // indirect + github.com/mattn/go-sqlite3 v1.14.28 // indirect + github.com/petermattis/goid v0.0.0-20250508124226-395b08cebbdb // 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,7 +40,7 @@ require ( go.mau.fi/libsignal v0.2.0 // indirect go.mau.fi/zeroconfig v0.1.3 // indirect golang.org/x/crypto v0.38.0 // indirect - golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect + golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect golang.org/x/sys v0.33.0 // indirect golang.org/x/text v0.25.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect diff --git a/go.sum b/go.sum index 4317b48..9c1ecc7 100644 --- a/go.sum +++ b/go.sum @@ -32,10 +32,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.27 h1:drZCnuvf37yPfs95E5jd9s3XhdVWLal+6BOK6qrv6IU= -github.com/mattn/go-sqlite3 v1.14.27/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/petermattis/goid v0.0.0-20250319124200-ccd6737f222a h1:S+AGcmAESQ0pXCUNnRH7V+bOUIgkSX5qVt2cNKCrm0Q= -github.com/petermattis/goid v0.0.0-20250319124200-ccd6737f222a/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A= +github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/petermattis/goid v0.0.0-20250508124226-395b08cebbdb h1:3PrKuO92dUTMrQ9dx0YNejC6U/Si6jqKmyQ9vWjwqR4= +github.com/petermattis/goid v0.0.0-20250508124226-395b08cebbdb/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= @@ -65,20 +65,20 @@ github.com/yuin/goldmark v1.7.10 h1:S+LrtBjRmqMac2UdtB6yyCEJm+UILZ2fefI4p7o0QpI= github.com/yuin/goldmark v1.7.10/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.8.7-0.20250427215252-d2d18a7e463c h1:qfJyMZq1pPyuXKoVWwHs6OmR9CzO3pHFRPYT/QpaaaA= -go.mau.fi/util v0.8.7-0.20250427215252-d2d18a7e463c/go.mod h1:uNB3UTXFbkpp7xL1M/WvQks90B/L4gvbLpbS0603KOE= +go.mau.fi/util v0.8.7-0.20250514115735-05fea4054bbd h1:+0zVSU9hJygQo9NDU/m8ObP6Kgl8buhBu7wiUHuzK6s= +go.mau.fi/util v0.8.7-0.20250514115735-05fea4054bbd/go.mod h1:Fg6GdUM0iMTlaRK4/TGg/3dSGmXBatbhtjpMwK2waVk= 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-20250514120219-ef9c1e91094e h1:7lboFyrizLaof8CBFIlqk1B50DVeVAq0vlMkBQVGE7A= -go.mau.fi/whatsmeow v0.0.0-20250514120219-ef9c1e91094e/go.mod h1:0b/hOeTpA38V89YTtE2yqMKfDfaffj8gMu+AswqprQI= +go.mau.fi/whatsmeow v0.0.0-20250514120708-22ca98ea604a h1:k9ggM9UTxHPRX8nUDMHdwqGFtNF/9RGkGUM1EYnKPMY= +go.mau.fi/whatsmeow v0.0.0-20250514120708-22ca98ea604a/go.mod h1:0b/hOeTpA38V89YTtE2yqMKfDfaffj8gMu+AswqprQI= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= -golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM= -golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8= -golang.org/x/image v0.26.0 h1:4XjIFEZWQmCZi6Wv8BoxsDhRU3RVnLX04dToTDAEPlY= -golang.org/x/image v0.26.0/go.mod h1:lcxbMFAovzpnJxzXS3nyL83K27tmqtKzIJpctK8YO5c= +golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI= +golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= +golang.org/x/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w= +golang.org/x/image v0.27.0/go.mod h1:xbdrClrAUway1MUTEZDq9mz/UpRwYAkFFNUslZtcB+g= golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= From 343aab50adc88d6f95a4f34a125374688e584873 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 15 May 2025 13:55:10 +0300 Subject: [PATCH 004/276] 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 94a3145..10d3bcc 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.8.7-0.20250514115735-05fea4054bbd go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250514120708-22ca98ea604a + go.mau.fi/whatsmeow v0.0.0-20250515105332-8c870897140e golang.org/x/image v0.27.0 golang.org/x/net v0.40.0 golang.org/x/sync v0.14.0 diff --git a/go.sum b/go.sum index 9c1ecc7..99b5e1a 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ go.mau.fi/util v0.8.7-0.20250514115735-05fea4054bbd h1:+0zVSU9hJygQo9NDU/m8ObP6K go.mau.fi/util v0.8.7-0.20250514115735-05fea4054bbd/go.mod h1:Fg6GdUM0iMTlaRK4/TGg/3dSGmXBatbhtjpMwK2waVk= 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-20250514120708-22ca98ea604a h1:k9ggM9UTxHPRX8nUDMHdwqGFtNF/9RGkGUM1EYnKPMY= -go.mau.fi/whatsmeow v0.0.0-20250514120708-22ca98ea604a/go.mod h1:0b/hOeTpA38V89YTtE2yqMKfDfaffj8gMu+AswqprQI= +go.mau.fi/whatsmeow v0.0.0-20250515105332-8c870897140e h1:j0voXCIzgx7lwVdrIiw1nBufnzCb08pXb1aN1qVerXQ= +go.mau.fi/whatsmeow v0.0.0-20250515105332-8c870897140e/go.mod h1:0b/hOeTpA38V89YTtE2yqMKfDfaffj8gMu+AswqprQI= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= From 98a695f15c65dd13e5a007714695ffb0edd7ea9e Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 15 May 2025 15:25:44 +0300 Subject: [PATCH 005/276] login: don't require entering phone number for relogin --- pkg/connector/login.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pkg/connector/login.go b/pkg/connector/login.go index 7a232d9..d0c1718 100644 --- a/pkg/connector/login.go +++ b/pkg/connector/login.go @@ -104,6 +104,7 @@ type WALogin struct { var ( _ bridgev2.LoginProcessDisplayAndWait = (*WALogin)(nil) _ bridgev2.LoginProcessUserInput = (*WALogin)(nil) + _ bridgev2.LoginProcessWithOverride = (*WALogin)(nil) ) const LoginConnectWait = 15 * time.Second @@ -149,6 +150,20 @@ func (wl *WALogin) Start(ctx context.Context) (*bridgev2.LoginStep, error) { return makeQRStep(wl.QRs[0]), nil } +func (wl *WALogin) StartWithOverride(ctx context.Context, old *bridgev2.UserLogin) (*bridgev2.LoginStep, error) { + step, err := wl.Start(ctx) + if err == nil && step != nil && old != nil && step.StepID == LoginStepIDPhoneNumber { + phoneNumber := fmt.Sprintf("+%s", old.ID) + wl.Log.Debug(). + Str("phone_number", phoneNumber). + Msg("Auto-submitting phone number for relogin") + return wl.SubmitUserInput(ctx, map[string]string{ + "phone_number": phoneNumber, + }) + } + return step, err +} + func (wl *WALogin) SubmitUserInput(ctx context.Context, input map[string]string) (*bridgev2.LoginStep, error) { ctx, cancel := context.WithTimeout(ctx, LoginConnectWait) defer cancel() From 906fa435808958843a4e75afb96dceecd6626697 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 16 May 2025 08:16:44 +0300 Subject: [PATCH 006/276] Bump version to v0.12.1 --- CHANGELOG.md | 11 +++++++++++ cmd/mautrix-whatsapp/main.go | 2 +- go.mod | 6 +++--- go.sum | 12 ++++++------ 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b7f100..f2f2485 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +# v0.12.1 (2025-05-16) + +* Added prefix to identify forwarded messages on WhatsApp. +* Updated mime type of unconverted animated stickers to `video/lottie+json` + which is now registered with IANA. +* Changed relogin command to not require entering phone number twice when using + phone code login. +* Fixed outgoing messages being rejected if they replied to a fake message + generated by the bridge. +* Fixed backfilling messages in existing portals after relogining. + # v0.12.0 (2025-04-16) * Migrated Signal session store to use new `@lid` identifiers to support future diff --git a/cmd/mautrix-whatsapp/main.go b/cmd/mautrix-whatsapp/main.go index 4867132..78df77b 100644 --- a/cmd/mautrix-whatsapp/main.go +++ b/cmd/mautrix-whatsapp/main.go @@ -23,7 +23,7 @@ var m = mxmain.BridgeMain{ Name: "mautrix-whatsapp", URL: "https://github.com/mautrix/whatsapp", Description: "A Matrix-WhatsApp puppeting bridge.", - Version: "0.12.0", + Version: "0.12.1", Connector: c, } diff --git a/go.mod b/go.mod index 10d3bcc..a0765d6 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/gorilla/websocket v1.5.0 github.com/lib/pq v1.10.9 github.com/rs/zerolog v1.34.0 - go.mau.fi/util v0.8.7-0.20250514115735-05fea4054bbd + go.mau.fi/util v0.8.7 go.mau.fi/webp v0.2.0 go.mau.fi/whatsmeow v0.0.0-20250515105332-8c870897140e golang.org/x/image v0.27.0 @@ -17,7 +17,7 @@ require ( golang.org/x/sync v0.14.0 google.golang.org/protobuf v1.36.6 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.23.4-0.20250509121614-a0191c8f5847 + maunium.net/go/mautrix v0.24.0 ) require ( @@ -36,7 +36,7 @@ require ( github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/sjson v1.2.5 // indirect - github.com/yuin/goldmark v1.7.10 // indirect + github.com/yuin/goldmark v1.7.11 // indirect go.mau.fi/libsignal v0.2.0 // indirect go.mau.fi/zeroconfig v0.1.3 // indirect golang.org/x/crypto v0.38.0 // indirect diff --git a/go.sum b/go.sum index 99b5e1a..499b996 100644 --- a/go.sum +++ b/go.sum @@ -61,12 +61,12 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= -github.com/yuin/goldmark v1.7.10 h1:S+LrtBjRmqMac2UdtB6yyCEJm+UILZ2fefI4p7o0QpI= -github.com/yuin/goldmark v1.7.10/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= +github.com/yuin/goldmark v1.7.11 h1:ZCxLyDMtz0nT2HFfsYG8WZ47Trip2+JyLysKcMYE5bo= +github.com/yuin/goldmark v1.7.11/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.8.7-0.20250514115735-05fea4054bbd h1:+0zVSU9hJygQo9NDU/m8ObP6Kgl8buhBu7wiUHuzK6s= -go.mau.fi/util v0.8.7-0.20250514115735-05fea4054bbd/go.mod h1:Fg6GdUM0iMTlaRK4/TGg/3dSGmXBatbhtjpMwK2waVk= +go.mau.fi/util v0.8.7 h1:ywKarPxouJQEEijTs4mPlxC7F4AWEKokEpWc+2TYy6c= +go.mau.fi/util v0.8.7/go.mod h1:j6R3cENakc1f8HpQeFl0N15UiSTcNmIfDBNJUbL71RY= 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-20250515105332-8c870897140e h1:j0voXCIzgx7lwVdrIiw1nBufnzCb08pXb1aN1qVerXQ= @@ -101,5 +101,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.23.4-0.20250509121614-a0191c8f5847 h1:umv6WzuHjAVLVJlNeGEUogZtcD+3I6/OIpFnThUBb+A= -maunium.net/go/mautrix v0.23.4-0.20250509121614-a0191c8f5847/go.mod h1:pT4G5RZQ+nLfKzsmeDa4NhHghOVTrasLLwY9tZ2mO08= +maunium.net/go/mautrix v0.24.0 h1:kBeyWhgL1W8/d8BEFlBSlgIpItPgP1l37hzF8cN3R70= +maunium.net/go/mautrix v0.24.0/go.mod h1:HqA1HUutQYJkrYRPkK64itARDz79PCec1oWVEB72HVQ= From c9f1e9cb77a01731b53459b3325a89c3cce32b3d Mon Sep 17 00:00:00 2001 From: Toni Spets Date: Wed, 21 May 2025 13:39:57 +0300 Subject: [PATCH 007/276] connector: Retry connect network errors in background --- go.mod | 2 +- go.sum | 4 ++-- pkg/connector/client.go | 1 + pkg/connector/config.go | 2 ++ 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index a0765d6..7cdbaec 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.8.7 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250515105332-8c870897140e + go.mau.fi/whatsmeow v0.0.0-20250521101145-106fc9aa085a golang.org/x/image v0.27.0 golang.org/x/net v0.40.0 golang.org/x/sync v0.14.0 diff --git a/go.sum b/go.sum index 499b996..992f5ca 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ go.mau.fi/util v0.8.7 h1:ywKarPxouJQEEijTs4mPlxC7F4AWEKokEpWc+2TYy6c= go.mau.fi/util v0.8.7/go.mod h1:j6R3cENakc1f8HpQeFl0N15UiSTcNmIfDBNJUbL71RY= 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-20250515105332-8c870897140e h1:j0voXCIzgx7lwVdrIiw1nBufnzCb08pXb1aN1qVerXQ= -go.mau.fi/whatsmeow v0.0.0-20250515105332-8c870897140e/go.mod h1:0b/hOeTpA38V89YTtE2yqMKfDfaffj8gMu+AswqprQI= +go.mau.fi/whatsmeow v0.0.0-20250521101145-106fc9aa085a h1:MeK4HVUXWZrDqyYzb4SfgJZHKb2l2OZ7NdkY0X71mGI= +go.mau.fi/whatsmeow v0.0.0-20250521101145-106fc9aa085a/go.mod h1:Qy3L3BNBcnxfrAQ09lmFMa0ItZfg8zl9DzxKrptzfU4= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= diff --git a/pkg/connector/client.go b/pkg/connector/client.go index b6eb376..9ba42b4 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -77,6 +77,7 @@ func (wa *WhatsAppConnector) LoadUserLogin(ctx context.Context, login *bridgev2. w.Client.GetMessageForRetry = w.trackNotFoundRetry w.Client.PreRetryCallback = w.trackFoundRetry w.Client.SetForceActiveDeliveryReceipts(wa.Config.ForceActiveDeliveryReceipts) + w.Client.InitialAutoReconnect = wa.Config.InitialAutoReconnect } 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 daffe99..e5a9209 100644 --- a/pkg/connector/config.go +++ b/pkg/connector/config.go @@ -48,6 +48,7 @@ type Config struct { DisableViewOnce bool `yaml:"disable_view_once"` ForceActiveDeliveryReceipts bool `yaml:"force_active_delivery_receipts"` DirectMediaAutoRequest bool `yaml:"direct_media_auto_request"` + InitialAutoReconnect bool `yaml:"initial_auto_reconnect"` AnimatedSticker msgconv.AnimatedStickerConfig `yaml:"animated_sticker"` @@ -112,6 +113,7 @@ func upgradeConfig(helper up.Helper) { helper.Copy(up.Bool, "disable_view_once") 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.Str, "animated_sticker", "target") helper.Copy(up.Int, "animated_sticker", "args", "width") From 513d5ecaf0a37d12cf655b4f4803def4b965bb6b Mon Sep 17 00:00:00 2001 From: Conan Date: Mon, 26 May 2025 23:51:46 +0800 Subject: [PATCH 008/276] directmedia: extract lottie zip (#793) --- pkg/connector/directmedia.go | 16 +++++++++++ pkg/msgconv/wa-media.go | 52 +++++++++++++++++++++++------------- 2 files changed, 49 insertions(+), 19 deletions(-) diff --git a/pkg/connector/directmedia.go b/pkg/connector/directmedia.go index df234ff..29c4462 100644 --- a/pkg/connector/directmedia.go +++ b/pkg/connector/directmedia.go @@ -21,6 +21,7 @@ import ( "encoding/json" "errors" "fmt" + "io" "net/http" "os" "sync" @@ -119,6 +120,21 @@ func (wa *WhatsAppConnector) Download(ctx context.Context, mediaID networkid.Med } else if err != nil { return err } + + if keys.MimeType == "application/was" { + if _, err := f.Seek(0, io.SeekStart); err != nil { + return 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) + } else if data, err := msgconv.ExtractAnimatedSticker(zipData); err != nil { + return 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) + } else if err := f.Truncate(int64(len(data))); err != nil { + return fmt.Errorf("failed to truncate animated sticker file: %w", err) + } + } + return nil }, // TODO? diff --git a/pkg/msgconv/wa-media.go b/pkg/msgconv/wa-media.go index d5f3be0..dc8b227 100644 --- a/pkg/msgconv/wa-media.go +++ b/pkg/msgconv/wa-media.go @@ -80,9 +80,14 @@ func (mc *MessageConverter) convertMediaMessage( Type: whatsmeow.GetMediaType(msg), SHA256: msg.GetFileSHA256(), EncSHA256: msg.GetFileEncSHA256(), + MimeType: msg.GetMimetype(), } if mc.DirectMedia { preparedMedia.FillFileName() + if preparedMedia.Info.MimeType == "application/was" { + preparedMedia.Info.MimeType = "video/lottie+json" + preparedMedia.FileName = "sticker.json" + } var err error portal := getPortal(ctx) preparedMedia.URL, err = portal.Bridge.Matrix.GenerateContentURI(ctx, waid.MakeMediaID(messageInfo, portal.Receiver)) @@ -123,6 +128,7 @@ type FailedMediaKeys struct { SHA256 []byte `json:"sha256"` EncSHA256 []byte `json:"enc_sha256"` DirectPath string `json:"direct_path,omitempty"` + MimeType string `json:"mime_type,omitempty"` } func (f *FailedMediaKeys) GetDirectPath() string { @@ -379,26 +385,9 @@ func (mc *MessageConverter) reuploadWhatsAppAttachment( } func (mc *MessageConverter) extractAnimatedSticker(fileInfo *PreparedMedia, data []byte) ([]byte, error) { - zipReader, err := zip.NewReader(bytes.NewReader(data), int64(len(data))) + data, err := ExtractAnimatedSticker(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 nil, err } fileInfo.Info.MimeType = "video/lottie+json" fileInfo.FileName = "sticker.json" @@ -501,3 +490,28 @@ 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 +} From 9c42210a2beec9955b955a0037490d5e9e2adea9 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 27 May 2025 11:44:13 +0300 Subject: [PATCH 009/276] dependencies: update mautrix-go --- go.mod | 2 +- go.sum | 4 ++-- pkg/msgconv/urlpreview.go | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 7cdbaec..00c8a0c 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( golang.org/x/sync v0.14.0 google.golang.org/protobuf v1.36.6 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.24.0 + maunium.net/go/mautrix v0.24.1-0.20250527083757-8a745c0d03ec ) require ( diff --git a/go.sum b/go.sum index 992f5ca..306f223 100644 --- a/go.sum +++ b/go.sum @@ -101,5 +101,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.24.0 h1:kBeyWhgL1W8/d8BEFlBSlgIpItPgP1l37hzF8cN3R70= -maunium.net/go/mautrix v0.24.0/go.mod h1:HqA1HUutQYJkrYRPkK64itARDz79PCec1oWVEB72HVQ= +maunium.net/go/mautrix v0.24.1-0.20250527083757-8a745c0d03ec h1:kn/SHMTE4FiafMFS9WKXzHQc/Z9yN09Aa7TiFOgYfSY= +maunium.net/go/mautrix v0.24.1-0.20250527083757-8a745c0d03ec/go.mod h1:HqA1HUutQYJkrYRPkK64itARDz79PCec1oWVEB72HVQ= diff --git a/pkg/msgconv/urlpreview.go b/pkg/msgconv/urlpreview.go index cb241c7..4bce93b 100644 --- a/pkg/msgconv/urlpreview.go +++ b/pkg/msgconv/urlpreview.go @@ -60,16 +60,16 @@ func (mc *MessageConverter) convertURLPreviewToBeeper(ctx context.Context, msg * thumbnailData = msg.JPEGThumbnail } if thumbnailData != nil { - output.ImageHeight = int(msg.GetThumbnailHeight()) - output.ImageWidth = int(msg.GetThumbnailWidth()) + output.ImageHeight = event.IntOrString(msg.GetThumbnailHeight()) + output.ImageWidth = event.IntOrString(msg.GetThumbnailWidth()) if output.ImageHeight == 0 || output.ImageWidth == 0 { src, _, err := image.Decode(bytes.NewReader(thumbnailData)) if err == nil { imageBounds := src.Bounds() - output.ImageWidth, output.ImageHeight = imageBounds.Max.X, imageBounds.Max.Y + output.ImageWidth, output.ImageHeight = event.IntOrString(imageBounds.Max.X), event.IntOrString(imageBounds.Max.Y) } } - output.ImageSize = len(thumbnailData) + output.ImageSize = event.IntOrString(len(thumbnailData)) output.ImageType = http.DetectContentType(thumbnailData) var err error output.ImageURL, output.ImageEncryption, err = getIntent(ctx).UploadMedia( From 24b61134ca796b70534bd65a8ff23be86fe10a61 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 29 May 2025 22:12:41 +0300 Subject: [PATCH 010/276] userinfo: fix setting identifiers of lid users --- pkg/connector/userinfo.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/connector/userinfo.go b/pkg/connector/userinfo.go index c0520b7..9a6c159 100644 --- a/pkg/connector/userinfo.go +++ b/pkg/connector/userinfo.go @@ -189,7 +189,7 @@ func (wa *WhatsAppClient) contactToUserInfo(ctx context.Context, jid types.JID, pnJID, err := wa.GetStore().LIDs.GetPNForLID(ctx, jid) if err != nil { zerolog.Ctx(ctx).Err(err).Stringer("lid", jid).Msg("Failed to get PN for LID") - } else { + } else if !pnJID.IsEmpty() { phone = "+" + pnJID.User extraContact, err := wa.GetStore().Contacts.GetContact(ctx, pnJID) if err != nil { @@ -216,11 +216,12 @@ func (wa *WhatsAppClient) contactToUserInfo(ctx context.Context, jid types.JID, ui := &bridgev2.UserInfo{ Name: ptr.Ptr(wa.Main.Config.FormatDisplayname(jid, phone, contact)), IsBot: ptr.Ptr(jid.IsBot()), - Identifiers: []string{fmt.Sprintf("tel:+%s", jid.User)}, ExtraUpdates: updateGhostLastSyncAt, } if jid.Server == types.BotServer { ui.Identifiers = []string{} + } else if phone != "" { + ui.Identifiers = []string{fmt.Sprintf("tel:%s", phone)} } if getAvatar { ui.ExtraUpdates = bridgev2.MergeExtraUpdaters(ui.ExtraUpdates, wa.fetchGhostAvatar) From 8ed1a47af41521db8044818ffd8bb7993ca8a27d Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 30 May 2025 12:09:30 +0300 Subject: [PATCH 011/276] backfill: short circuits sleeps if context is canceled --- pkg/connector/backfill.go | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/pkg/connector/backfill.go b/pkg/connector/backfill.go index 56cb847..c7c4bcf 100644 --- a/pkg/connector/backfill.go +++ b/pkg/connector/backfill.go @@ -192,13 +192,15 @@ func (wa *WhatsAppClient) createPortalsFromHistorySync(ctx context.Context) { Logger() ctx = log.WithContext(ctx) limit := wa.Main.Config.HistorySync.MaxInitialConversations - log.Info().Int("limit", limit).Msg("Creating portals from history sync") conversations, err := wa.Main.DB.Conversation.GetRecent(ctx, wa.UserLogin.ID, limit) if err != nil { log.Err(err).Msg("Failed to get recent conversations from database") return } - log.Info().Int("conversation_count", len(conversations)).Msg("Creating portals from history sync") + log.Info(). + Int("limit", limit). + Int("conversation_count", len(conversations)). + Msg("Creating portals from history sync") rateLimitErrors := 0 var wg sync.WaitGroup wg.Add(len(conversations)) @@ -220,7 +222,12 @@ func (wa *WhatsAppClient) createPortalsFromHistorySync(ctx context.Context) { continue } // TODO can the chat info fetch be avoided entirely? - time.Sleep(time.Duration(rateLimitErrors) * time.Second) + select { + case <-time.After(time.Duration(rateLimitErrors) * time.Second): + case <-ctx.Done(): + log.Warn().Err(ctx.Err()).Msg("Context cancelled, stopping history sync portal creation") + return + } wrappedInfo, err := wa.getChatInfo(ctx, conv.ChatJID, conv) if errors.Is(err, whatsmeow.ErrNotInGroup) { log.Debug().Stringer("chat_jid", conv.ChatJID). @@ -241,7 +248,12 @@ func (wa *WhatsAppClient) createPortalsFromHistorySync(ctx context.Context) { log.Err(err).Stringer("chat_jid", conv.ChatJID). Int("error_count", rateLimitErrors). Msg("Ratelimit error getting chat info, retrying after sleep") - time.Sleep(time.Duration(rateLimitErrors) * time.Minute) + select { + case <-time.After(time.Duration(rateLimitErrors) * time.Second): + case <-ctx.Done(): + log.Warn().Err(ctx.Err()).Msg("Context cancelled, stopping history sync portal creation") + return + } continue } else if err != nil { log.Err(err).Stringer("chat_jid", conv.ChatJID).Msg("Failed to get chat info") From 57d6ec721ee7eaec41e87bf6c4504e6e34d3b05b Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 30 May 2025 17:15:36 +0300 Subject: [PATCH 012/276] msgconv/from-whatsapp: fix mentions of Matrix users in lid groups --- pkg/connector/id.go | 2 +- pkg/msgconv/from-whatsapp.go | 30 ++++++++++++++++++++++++------ 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/pkg/connector/id.go b/pkg/connector/id.go index 3b5d23d..ea4e4c8 100644 --- a/pkg/connector/id.go +++ b/pkg/connector/id.go @@ -33,7 +33,7 @@ func (wa *WhatsAppClient) makeEventSender(id types.JID) bridgev2.EventSender { return bridgev2.EventSender{ IsFromMe: id.User == wa.GetStore().GetJID().User || id.User == wa.GetStore().GetLID().User, Sender: waid.MakeUserID(id), - SenderLogin: waid.MakeUserLoginID(id), + SenderLogin: waid.MakeUserLoginID(id), // TODO add support for lids here by looking up pn jid } } diff --git a/pkg/msgconv/from-whatsapp.go b/pkg/msgconv/from-whatsapp.go index 3bb824e..7ee11ee 100644 --- a/pkg/msgconv/from-whatsapp.go +++ b/pkg/msgconv/from-whatsapp.go @@ -61,14 +61,32 @@ func getPortal(ctx context.Context) *bridgev2.Portal { return ctx.Value(contextKeyPortal).(*bridgev2.Portal) } -func (mc *MessageConverter) getBasicUserInfo(ctx context.Context, user networkid.UserID) (id.UserID, string, error) { - ghost, err := mc.Bridge.GetGhostByID(ctx, user) +func (mc *MessageConverter) getBasicUserInfo(ctx context.Context, user types.JID) (id.UserID, string, error) { + ghost, err := mc.Bridge.GetGhostByID(ctx, waid.MakeUserID(user)) if err != nil { return "", "", fmt.Errorf("failed to get ghost by ID: %w", err) } - login := mc.Bridge.GetCachedUserLoginByID(networkid.UserLoginID(user)) - if login != nil { - return login.UserMXID, ghost.Name, nil + var pnJID types.JID + if user.Server == types.DefaultUserServer { + pnJID = user + } else if user.Server == types.HiddenUserServer { + cli := getClient(ctx) + if user.User == cli.Store.GetLID().User { + pnJID = cli.Store.GetJID() + } else { + pnJID, err = cli.Store.LIDs.GetPNForLID(ctx, user) + if err != nil { + zerolog.Ctx(ctx).Err(err). + Stringer("lid", user). + Msg("Failed to get PN for LID in mention bridging") + } + } + } + if !pnJID.IsEmpty() { + login := mc.Bridge.GetCachedUserLoginByID(waid.MakeUserLoginID(pnJID)) + if login != nil { + return login.UserMXID, ghost.Name, nil + } } return ghost.Intent.GetMXID(), ghost.Name, nil } @@ -84,7 +102,7 @@ func (mc *MessageConverter) addMentions(ctx context.Context, mentionedJID []stri zerolog.Ctx(ctx).Err(err).Str("jid", jid).Msg("Failed to parse mentioned JID") continue } - mxid, displayname, err := mc.getBasicUserInfo(ctx, waid.MakeUserID(parsed)) + mxid, displayname, err := mc.getBasicUserInfo(ctx, parsed) if err != nil { zerolog.Ctx(ctx).Err(err).Str("jid", jid).Msg("Failed to get user info") continue From 94f4678a99a7d31c8fb4f4aba65e3b8e294a213f Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 3 Jun 2025 14:15:51 +0300 Subject: [PATCH 013/276] 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 00c8a0c..4d91231 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.8.7 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250521101145-106fc9aa085a + go.mau.fi/whatsmeow v0.0.0-20250603110639-ff268dee27a7 golang.org/x/image v0.27.0 golang.org/x/net v0.40.0 golang.org/x/sync v0.14.0 diff --git a/go.sum b/go.sum index 306f223..d4601f7 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ go.mau.fi/util v0.8.7 h1:ywKarPxouJQEEijTs4mPlxC7F4AWEKokEpWc+2TYy6c= go.mau.fi/util v0.8.7/go.mod h1:j6R3cENakc1f8HpQeFl0N15UiSTcNmIfDBNJUbL71RY= 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-20250521101145-106fc9aa085a h1:MeK4HVUXWZrDqyYzb4SfgJZHKb2l2OZ7NdkY0X71mGI= -go.mau.fi/whatsmeow v0.0.0-20250521101145-106fc9aa085a/go.mod h1:Qy3L3BNBcnxfrAQ09lmFMa0ItZfg8zl9DzxKrptzfU4= +go.mau.fi/whatsmeow v0.0.0-20250603110639-ff268dee27a7 h1:RxtSEtqm/Eib5BBB7/joyK9yqZUSTv0c0qBuz+gZXPM= +go.mau.fi/whatsmeow v0.0.0-20250603110639-ff268dee27a7/go.mod h1:Qy3L3BNBcnxfrAQ09lmFMa0ItZfg8zl9DzxKrptzfU4= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= From 03b42f0d51036bc46f13c19e7aaa5ecf70102fc7 Mon Sep 17 00:00:00 2001 From: Nick Mills-Barrett Date: Tue, 3 Jun 2025 10:07:28 +0100 Subject: [PATCH 014/276] userinfo: expose portal resync configuration settings Enables tweaking the sync interval/etc for non-cloud environments. --- pkg/connector/userinfo.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/pkg/connector/userinfo.go b/pkg/connector/userinfo.go index 9a6c159..1fed10f 100644 --- a/pkg/connector/userinfo.go +++ b/pkg/connector/userinfo.go @@ -20,11 +20,12 @@ import ( "go.mau.fi/mautrix-whatsapp/pkg/waid" ) -const resyncMinInterval = 7 * 24 * time.Hour -const resyncLoopInterval = 4 * time.Hour +var ResyncMinInterval = 7 * 24 * time.Hour +var ResyncLoopInterval = 4 * time.Hour +var ResyncJitterSeconds = 3600 func (wa *WhatsAppClient) EnqueueGhostResync(ghost *bridgev2.Ghost) { - if ghost.Metadata.(*waid.GhostMetadata).LastSync.Add(resyncMinInterval).After(time.Now()) { + if ghost.Metadata.(*waid.GhostMetadata).LastSync.Add(ResyncMinInterval).After(time.Now()) { return } wa.resyncQueueLock.Lock() @@ -41,7 +42,7 @@ func (wa *WhatsAppClient) EnqueueGhostResync(ghost *bridgev2.Ghost) { func (wa *WhatsAppClient) EnqueuePortalResync(portal *bridgev2.Portal) { jid, _ := waid.ParsePortalID(portal.ID) - if jid.Server != types.GroupServer || portal.Metadata.(*waid.PortalMetadata).LastSync.Add(resyncMinInterval).After(time.Now()) { + if jid.Server != types.GroupServer || portal.Metadata.(*waid.PortalMetadata).LastSync.Add(ResyncMinInterval).After(time.Now()) { return } wa.resyncQueueLock.Lock() @@ -58,7 +59,7 @@ func (wa *WhatsAppClient) EnqueuePortalResync(portal *bridgev2.Portal) { func (wa *WhatsAppClient) ghostResyncLoop(ctx context.Context) { log := wa.UserLogin.Log.With().Str("action", "ghost resync loop").Logger() ctx = log.WithContext(ctx) - wa.nextResync = time.Now().Add(resyncLoopInterval).Add(-time.Duration(rand.IntN(3600)) * time.Second) + wa.nextResync = time.Now().Add(ResyncLoopInterval).Add(-time.Duration(rand.IntN(ResyncJitterSeconds)) * time.Second) timer := time.NewTimer(time.Until(wa.nextResync)) log.Info().Time("first_resync", wa.nextResync).Msg("Ghost resync queue starting") for { @@ -81,7 +82,7 @@ func (wa *WhatsAppClient) ghostResyncLoop(ctx context.Context) { func (wa *WhatsAppClient) rotateResyncQueue() map[types.JID]resyncQueueItem { wa.resyncQueueLock.Lock() defer wa.resyncQueueLock.Unlock() - wa.nextResync = time.Now().Add(resyncLoopInterval) + wa.nextResync = time.Now().Add(ResyncLoopInterval) if len(wa.resyncQueue) == 0 { return nil } @@ -108,7 +109,7 @@ func (wa *WhatsAppClient) doGhostResync(ctx context.Context, queue map[types.JID } else if item.portal != nil { lastSync = item.portal.Metadata.(*waid.PortalMetadata).LastSync.Time } - if lastSync.Add(resyncMinInterval).After(time.Now()) { + if lastSync.Add(ResyncMinInterval).After(time.Now()) { log.Debug(). Stringer("jid", jid). Time("last_sync", lastSync). From cbcc302f8900e8921166c0c9eae31b8a1f0a8264 Mon Sep 17 00:00:00 2001 From: Nick Mills-Barrett Date: Tue, 3 Jun 2025 10:08:10 +0100 Subject: [PATCH 015/276] client: immediately sync portal and DM ghost when viewed Implements the `bridgev2.ChatViewingNetworkAPI` API and triggers chat or ghost resync when a chat is viewed. --- pkg/connector/client.go | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/pkg/connector/client.go b/pkg/connector/client.go index 9ba42b4..642072b 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -113,6 +113,7 @@ var ( _ bridgev2.NetworkAPI = (*WhatsAppClient)(nil) _ bridgev2.PushableNetworkAPI = (*WhatsAppClient)(nil) _ bridgev2.BackgroundSyncingNetworkAPI = (*WhatsAppClient)(nil) + _ bridgev2.ChatViewingNetworkAPI = (*WhatsAppClient)(nil) ) var pushCfg = &bridgev2.PushConfig{ @@ -373,3 +374,33 @@ func (wa *WhatsAppClient) syncRemoteProfile(ctx context.Context, ghost *bridgev2 } zerolog.Ctx(ctx).Info().Msg("Remote profile updated") } + +func (wa *WhatsAppClient) HandleMatrixViewingChat(ctx context.Context, msg *bridgev2.MatrixViewingChat) error { + if msg.Portal.Metadata.(*waid.PortalMetadata).LastSync.Add(5 * time.Minute).After(time.Now()) { + // If we resynced this portal within the last 5 minutes, don't do it again + return nil + } + + // Reset, but don't save, portal last sync time for immediate sync now + msg.Portal.Metadata.(*waid.PortalMetadata).LastSync.Time = time.Time{} + // Enqueue for the sync, don't block on it completing + wa.EnqueuePortalResync(msg.Portal) + + if msg.Portal.OtherUserID != "" { + // If this is a DM, also sync the ghost of the other user immediately + ghost, err := wa.Main.Bridge.GetExistingGhostByID(ctx, msg.Portal.OtherUserID) + if err != nil { + return fmt.Errorf("failed to get ghost for sync: %w", err) + } else if ghost == nil { + zerolog.Ctx(ctx).Warn(). + Str("other_user_id", string(msg.Portal.OtherUserID)). + Msg("No ghost found for other user in portal") + } else { + // Reset, but don't save, portal last sync time for immediate sync now + ghost.Metadata.(*waid.GhostMetadata).LastSync.Time = time.Time{} + wa.EnqueueGhostResync(ghost) + } + } + + return nil +} From 2fa26d5161e335b79db44fecaa5188e3da603baf Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 3 Jun 2025 16:54:30 +0300 Subject: [PATCH 016/276] legacymigrate: drop invalid disappearing message rows --- cmd/mautrix-whatsapp/legacymigrate.sql | 3 ++- cmd/mautrix-whatsapp/main.go | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/cmd/mautrix-whatsapp/legacymigrate.sql b/cmd/mautrix-whatsapp/legacymigrate.sql index 013519f..e3b9c3e 100644 --- a/cmd/mautrix-whatsapp/legacymigrate.sql +++ b/cmd/mautrix-whatsapp/legacymigrate.sql @@ -193,7 +193,8 @@ SELECT 'after_read', expire_in * 1000000, -- timer expire_at * 1000000 -- disappear_at -FROM disappearing_message_old; +FROM disappearing_message_old +WHERE room_id IN (SELECT mxid FROM portal WHERE mxid IS NOT NULL); INSERT INTO whatsapp_poll_option_id (bridge_id, msg_mxid, opt_id, opt_hash) SELECT '', msg_mxid, opt_id, opt_hash diff --git a/cmd/mautrix-whatsapp/main.go b/cmd/mautrix-whatsapp/main.go index 78df77b..a0542f2 100644 --- a/cmd/mautrix-whatsapp/main.go +++ b/cmd/mautrix-whatsapp/main.go @@ -35,8 +35,8 @@ func main() { "v0.8.6", "v0.11.0", m.LegacyMigrateWithAnotherUpgrader( - legacyMigrateRenameTables, legacyMigrateCopyData, 17, - upgrades.Table, "whatsapp_version", 3, + legacyMigrateRenameTables, legacyMigrateCopyData, 21, + upgrades.Table, "whatsapp_version", 5, ), true, ) From c47fced2fdfd0da1d4b86c80fc1e4611d932b478 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 3 Jun 2025 22:23:00 +0530 Subject: [PATCH 017/276] handlewhatsapp: add queue for incoming history sync notifications (#798) --- go.mod | 2 +- go.sum | 4 +- pkg/connector/backfill.go | 86 ++++++++++++++++++- pkg/connector/client.go | 3 + pkg/connector/handlewhatsapp.go | 6 ++ pkg/connector/wadb/database.go | 5 ++ pkg/connector/wadb/hsnotif.go | 78 +++++++++++++++++ .../wadb/upgrades/00-latest-schema.sql | 17 +++- .../upgrades/05-history-sync-notification.sql | 15 ++++ 9 files changed, 208 insertions(+), 8 deletions(-) create mode 100644 pkg/connector/wadb/hsnotif.go create mode 100644 pkg/connector/wadb/upgrades/05-history-sync-notification.sql diff --git a/go.mod b/go.mod index 4d91231..30fdc59 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.8.7 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250603110639-ff268dee27a7 + go.mau.fi/whatsmeow v0.0.0-20250603144927-2b269a5d396a golang.org/x/image v0.27.0 golang.org/x/net v0.40.0 golang.org/x/sync v0.14.0 diff --git a/go.sum b/go.sum index d4601f7..24e135f 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ go.mau.fi/util v0.8.7 h1:ywKarPxouJQEEijTs4mPlxC7F4AWEKokEpWc+2TYy6c= go.mau.fi/util v0.8.7/go.mod h1:j6R3cENakc1f8HpQeFl0N15UiSTcNmIfDBNJUbL71RY= 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-20250603110639-ff268dee27a7 h1:RxtSEtqm/Eib5BBB7/joyK9yqZUSTv0c0qBuz+gZXPM= -go.mau.fi/whatsmeow v0.0.0-20250603110639-ff268dee27a7/go.mod h1:Qy3L3BNBcnxfrAQ09lmFMa0ItZfg8zl9DzxKrptzfU4= +go.mau.fi/whatsmeow v0.0.0-20250603144927-2b269a5d396a h1:LkppC9KJ5BQb+A7PQbGL+b8sKMpRO8QJcRnl6w93gy4= +go.mau.fi/whatsmeow v0.0.0-20250603144927-2b269a5d396a/go.mod h1:Qy3L3BNBcnxfrAQ09lmFMa0ItZfg8zl9DzxKrptzfU4= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= diff --git a/pkg/connector/backfill.go b/pkg/connector/backfill.go index c7c4bcf..9a982ea 100644 --- a/pkg/connector/backfill.go +++ b/pkg/connector/backfill.go @@ -38,12 +38,35 @@ func (wa *WhatsAppClient) historySyncLoop(ctx context.Context) { } else { dispatchTimer.Stop() } + if wa.Client.ManualHistorySyncDownload { + // Wake up the queue once to check if there are pending notifications + select { + case wa.historySyncWakeup <- struct{}{}: + default: + } + } wa.UserLogin.Log.Debug().Msg("Starting history sync loop") for { select { case evt := <-wa.historySyncs: dispatchTimer.Stop() - wa.handleWAHistorySync(ctx, evt) + _ = wa.handleWAHistorySync(ctx, evt, false) + dispatchTimer.Reset(historySyncDispatchWait) + case <-wa.historySyncWakeup: + dispatchTimer.Stop() + notif, rowid, err := wa.Main.DB.HSNotif.GetNext(ctx, wa.UserLogin.ID) + if err != nil { + wa.UserLogin.Log.Err(err).Msg("Failed to get next history sync notification") + } else if notif == nil { + wa.UserLogin.Log.Debug().Msg("No more queued history sync notifications") + } else { + wa.downloadAndSaveWAHistorySyncData(ctx, notif, rowid) + // Continue waking up the loop until all queued notifications are processed + select { + case wa.historySyncWakeup <- struct{}{}: + default: + } + } dispatchTimer.Reset(historySyncDispatchWait) case <-dispatchTimer.C: wa.createPortalsFromHistorySync(ctx) @@ -54,10 +77,56 @@ func (wa *WhatsAppClient) historySyncLoop(ctx context.Context) { } } -func (wa *WhatsAppClient) handleWAHistorySync(ctx context.Context, evt *waHistorySync.HistorySync) { - if evt == nil || evt.SyncType == nil { +func (wa *WhatsAppClient) saveWAHistorySyncNotification(ctx context.Context, evt *waE2E.HistorySyncNotification) { + err := wa.Main.DB.HSNotif.Put(ctx, wa.UserLogin.ID, evt) + if err != nil { + wa.UserLogin.Log.Err(err).Msg("Failed to store history sync notification in queue") return } + wa.UserLogin.Log.Debug(). + Stringer("sync_type", evt.GetSyncType()). + Uint32("chunk_order", evt.GetChunkOrder()). + Uint32("progress", evt.GetProgress()). + Msg("Stored history sync notification in queue") + select { + case wa.historySyncWakeup <- struct{}{}: + default: + } +} + +func (wa *WhatsAppClient) downloadAndSaveWAHistorySyncData(ctx context.Context, evt *waE2E.HistorySyncNotification, rowid int) { + log := wa.UserLogin.Log.With(). + Str("action", "download history sync"). + Stringer("sync_type", evt.GetSyncType()). + Uint32("chunk_order", evt.GetChunkOrder()). + Uint32("progress", evt.GetProgress()). + Logger() + log.Debug().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 + } + err = wa.Main.DB.DoTxn(ctx, nil, func(ctx context.Context) error { + err := wa.handleWAHistorySync(ctx, blob, true) + if err != nil { + return err + } + err = wa.Main.DB.HSNotif.Delete(ctx, rowid) + if err != nil { + return fmt.Errorf("failed to delete queued history sync notification: %w", err) + } + return nil + }) + if err != nil { + log.Err(err).Msg("Failed to store history sync notification data") + } +} + +func (wa *WhatsAppClient) handleWAHistorySync(ctx context.Context, evt *waHistorySync.HistorySync, stopOnError bool) error { + if evt == nil || evt.SyncType == nil { + return nil + } log := wa.UserLogin.Log.With(). Str("action", "store history sync"). Stringer("sync_type", evt.GetSyncType()). @@ -76,12 +145,13 @@ func (wa *WhatsAppClient) handleWAHistorySync(ctx context.Context, evt *waHistor Int("recent_sticker_count", len(evt.GetRecentStickers())). Int("past_participant_count", len(evt.GetPastParticipants())). Msg("Ignoring history sync") - return + return nil } log.Info(). Int("conversation_count", len(evt.GetConversations())). Int("past_participant_count", len(evt.GetPastParticipants())). Msg("Storing history sync") + start := time.Now() successfullySavedTotal := 0 failedToSaveTotal := 0 totalMessageCount := 0 @@ -167,11 +237,17 @@ func (wa *WhatsAppClient) handleWAHistorySync(ctx context.Context, evt *waHistor if len(messages) > 0 { err = wa.Main.DB.Conversation.Put(ctx, wadb.NewConversation(wa.UserLogin.ID, jid, conv, maxTime)) if err != nil { + if stopOnError { + return fmt.Errorf("failed to save conversation metadata for %s: %w", jid, err) + } log.Err(err).Msg("Failed to save conversation metadata") continue } err = wa.Main.DB.Message.Put(ctx, wa.UserLogin.ID, jid, messages) if err != nil { + if stopOnError { + return fmt.Errorf("failed to save messages in %s: %w", jid, err) + } log.Err(err).Msg("Failed to save messages") failedToSaveTotal += len(messages) } else { @@ -183,7 +259,9 @@ func (wa *WhatsAppClient) handleWAHistorySync(ctx context.Context, evt *waHistor Int("total_saved_count", successfullySavedTotal). Int("total_failed_count", failedToSaveTotal). Int("total_message_count", totalMessageCount). + Dur("duration", time.Since(start)). Msg("Finished storing history sync") + return nil } func (wa *WhatsAppClient) createPortalsFromHistorySync(ctx context.Context) { diff --git a/pkg/connector/client.go b/pkg/connector/client.go index 642072b..434fe34 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -47,6 +47,7 @@ func (wa *WhatsAppConnector) LoadUserLogin(ctx context.Context, login *bridgev2. UserLogin: login, 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), @@ -72,6 +73,7 @@ func (wa *WhatsAppConnector) LoadUserLogin(ctx context.Context, login *bridgev2. if bridgev2.PortalEventBuffer == 0 { w.Client.SynchronousAck = true w.Client.EnableDecryptedEventBuffer = true + w.Client.ManualHistorySyncDownload = true } w.Client.AutomaticMessageRerequestFromPhone = true w.Client.GetMessageForRetry = w.trackNotFoundRetry @@ -98,6 +100,7 @@ type WhatsAppClient struct { JID types.JID historySyncs chan *waHistorySync.HistorySync + historySyncWakeup chan struct{} stopLoops atomic.Pointer[context.CancelFunc] resyncQueue map[types.JID]resyncQueueItem resyncQueueLock sync.Mutex diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index 196905e..8df3bcc 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -263,6 +263,12 @@ func (wa *WhatsAppClient) handleWAMessage(ctx context.Context, evt *events.Messa 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 && + wa.Client.ManualHistorySyncDownload { + wa.saveWAHistorySyncNotification(ctx, evt.Message.ProtocolMessage.HistorySyncNotification) + } parsedMessageType := getMessageType(evt.Message) if parsedMessageType == "ignore" || strings.HasPrefix(parsedMessageType, "unknown_protocol_") { return diff --git a/pkg/connector/wadb/database.go b/pkg/connector/wadb/database.go index f6f2655..5090980 100644 --- a/pkg/connector/wadb/database.go +++ b/pkg/connector/wadb/database.go @@ -14,6 +14,7 @@ type Database struct { Message *MessageQuery PollOption *PollOptionQuery MediaRequest *MediaRequestQuery + HSNotif *HistorySyncNotificationQuery } func New(bridgeID networkid.BridgeID, db *dbutil.Database, log zerolog.Logger) *Database { @@ -40,5 +41,9 @@ func New(bridgeID networkid.BridgeID, db *dbutil.Database, log zerolog.Logger) * return &MediaRequest{} }), }, + HSNotif: &HistorySyncNotificationQuery{ + BridgeID: bridgeID, + Database: db, + }, } } diff --git a/pkg/connector/wadb/hsnotif.go b/pkg/connector/wadb/hsnotif.go new file mode 100644 index 0000000..0221cdd --- /dev/null +++ b/pkg/connector/wadb/hsnotif.go @@ -0,0 +1,78 @@ +// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge. +// Copyright (C) 2025 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 wadb + +import ( + "context" + "database/sql" + "errors" + + "go.mau.fi/util/dbutil" + "google.golang.org/protobuf/proto" + "maunium.net/go/mautrix/bridgev2/networkid" + + "go.mau.fi/whatsmeow/proto/waE2E" +) + +type HistorySyncNotificationQuery struct { + BridgeID networkid.BridgeID + *dbutil.Database +} + +const ( + putHSNotificationQuery = ` + INSERT INTO whatsapp_history_sync_notification (bridge_id, user_login_id, data) + VALUES ($1, $2, $3) + ` + getNextHSNotificationQuery = ` + SELECT rowid, data FROM whatsapp_history_sync_notification + WHERE bridge_id=$1 AND user_login_id=$2 + ` + deleteHSNotificationQuery = ` + DELETE FROM whatsapp_history_sync_notification WHERE rowid=$1 + ` +) + +func (hsnq *HistorySyncNotificationQuery) Put(ctx context.Context, loginID networkid.UserLoginID, notif *waE2E.HistorySyncNotification) error { + notifBytes, err := proto.Marshal(notif) + if err != nil { + return err + } + _, err = hsnq.Exec(ctx, putHSNotificationQuery, hsnq.BridgeID, loginID, notifBytes) + return err +} + +func (hsnq *HistorySyncNotificationQuery) GetNext(ctx context.Context, loginID networkid.UserLoginID) (*waE2E.HistorySyncNotification, int, error) { + var notifBytes []byte + var rowid int + err := hsnq.QueryRow(ctx, getNextHSNotificationQuery, hsnq.BridgeID, loginID).Scan(&rowid, ¬ifBytes) + if errors.Is(err, sql.ErrNoRows) { + return nil, 0, nil + } else if err != nil { + return nil, 0, err + } + var notif waE2E.HistorySyncNotification + if err = proto.Unmarshal(notifBytes, ¬if); err != nil { + return nil, 0, err + } + return ¬if, rowid, nil +} + +func (hsnq *HistorySyncNotificationQuery) Delete(ctx context.Context, rowid int) error { + _, err := hsnq.Exec(ctx, deleteHSNotificationQuery, rowid) + return err +} diff --git a/pkg/connector/wadb/upgrades/00-latest-schema.sql b/pkg/connector/wadb/upgrades/00-latest-schema.sql index dbc41d1..f99bad4 100644 --- a/pkg/connector/wadb/upgrades/00-latest-schema.sql +++ b/pkg/connector/wadb/upgrades/00-latest-schema.sql @@ -1,4 +1,4 @@ --- v0 -> v4 (compatible with v3+): Latest revision +-- v0 -> v5 (compatible with v3+): Latest revision CREATE TABLE whatsapp_poll_option_id ( bridge_id TEXT NOT NULL, @@ -74,3 +74,18 @@ CREATE TABLE whatsapp_media_backfill_request ( ); CREATE INDEX whatsapp_media_backfill_request_portal_idx ON whatsapp_media_backfill_request (bridge_id, portal_id, portal_receiver); CREATE INDEX whatsapp_media_backfill_request_message_idx ON whatsapp_media_backfill_request (bridge_id, portal_receiver, message_id, _part_id); + +CREATE TABLE whatsapp_history_sync_notification ( + -- only: sqlite (line commented) +-- rowid INTEGER PRIMARY KEY, + -- only: postgres + rowid BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + + bridge_id TEXT NOT NULL, + user_login_id TEXT NOT NULL, + data bytea NOT NULL, + + CONSTRAINT whatsapp_history_sync_notification_user_login_fkey FOREIGN KEY (bridge_id, user_login_id) + REFERENCES user_login (bridge_id, id) ON UPDATE CASCADE ON DELETE CASCADE +); +CREATE INDEX whatsapp_history_sync_notification_login_idx ON whatsapp_history_sync_notification (bridge_id, user_login_id); diff --git a/pkg/connector/wadb/upgrades/05-history-sync-notification.sql b/pkg/connector/wadb/upgrades/05-history-sync-notification.sql new file mode 100644 index 0000000..d444d43 --- /dev/null +++ b/pkg/connector/wadb/upgrades/05-history-sync-notification.sql @@ -0,0 +1,15 @@ +-- v5 (compatible with v3+): Add buffer for history sync notifications +CREATE TABLE whatsapp_history_sync_notification ( + -- only: sqlite (line commented) +-- rowid INTEGER PRIMARY KEY, + -- only: postgres + rowid BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + + bridge_id TEXT NOT NULL, + user_login_id TEXT NOT NULL, + data bytea NOT NULL, + + CONSTRAINT whatsapp_history_sync_notification_user_login_fkey FOREIGN KEY (bridge_id, user_login_id) + REFERENCES user_login (bridge_id, id) ON UPDATE CASCADE ON DELETE CASCADE +); +CREATE INDEX whatsapp_history_sync_notification_login_idx ON whatsapp_history_sync_notification (bridge_id, user_login_id); From f5b835e042ee943aa232f306bca85dcc34a25066 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 4 Jun 2025 00:43:51 +0300 Subject: [PATCH 018/276] client: fix viewing chat handler --- 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 434fe34..300bbec 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -379,7 +379,7 @@ func (wa *WhatsAppClient) syncRemoteProfile(ctx context.Context, ghost *bridgev2 } func (wa *WhatsAppClient) HandleMatrixViewingChat(ctx context.Context, msg *bridgev2.MatrixViewingChat) error { - if msg.Portal.Metadata.(*waid.PortalMetadata).LastSync.Add(5 * time.Minute).After(time.Now()) { + if msg.Portal == nil || msg.Portal.Metadata.(*waid.PortalMetadata).LastSync.Add(5*time.Minute).After(time.Now()) { // If we resynced this portal within the last 5 minutes, don't do it again return nil } From 30f0ac71fbf0af8a28c82506b2b50528abdeeda3 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 5 Jun 2025 14:29:51 +0300 Subject: [PATCH 019/276] dependencies: update whatsmeow to use bridge background context --- go.mod | 4 ++-- go.sum | 8 ++++---- pkg/connector/client.go | 1 + 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 30fdc59..8ea054c 100644 --- a/go.mod +++ b/go.mod @@ -11,13 +11,13 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.8.7 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250603144927-2b269a5d396a + go.mau.fi/whatsmeow v0.0.0-20250605112842-73d070ff1c78 golang.org/x/image v0.27.0 golang.org/x/net v0.40.0 golang.org/x/sync v0.14.0 google.golang.org/protobuf v1.36.6 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.24.1-0.20250527083757-8a745c0d03ec + maunium.net/go/mautrix v0.24.1-0.20250605042548-d228995d718a ) require ( diff --git a/go.sum b/go.sum index 24e135f..c444ef4 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ go.mau.fi/util v0.8.7 h1:ywKarPxouJQEEijTs4mPlxC7F4AWEKokEpWc+2TYy6c= go.mau.fi/util v0.8.7/go.mod h1:j6R3cENakc1f8HpQeFl0N15UiSTcNmIfDBNJUbL71RY= 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-20250603144927-2b269a5d396a h1:LkppC9KJ5BQb+A7PQbGL+b8sKMpRO8QJcRnl6w93gy4= -go.mau.fi/whatsmeow v0.0.0-20250603144927-2b269a5d396a/go.mod h1:Qy3L3BNBcnxfrAQ09lmFMa0ItZfg8zl9DzxKrptzfU4= +go.mau.fi/whatsmeow v0.0.0-20250605112842-73d070ff1c78 h1:LGAZWk6ofDh6SrkVX/y/yJHGAv8ThHJTh0WFfHMZ1oI= +go.mau.fi/whatsmeow v0.0.0-20250605112842-73d070ff1c78/go.mod h1:Qy3L3BNBcnxfrAQ09lmFMa0ItZfg8zl9DzxKrptzfU4= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= @@ -101,5 +101,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.24.1-0.20250527083757-8a745c0d03ec h1:kn/SHMTE4FiafMFS9WKXzHQc/Z9yN09Aa7TiFOgYfSY= -maunium.net/go/mautrix v0.24.1-0.20250527083757-8a745c0d03ec/go.mod h1:HqA1HUutQYJkrYRPkK64itARDz79PCec1oWVEB72HVQ= +maunium.net/go/mautrix v0.24.1-0.20250605042548-d228995d718a h1:XsaGeWjlp2WJVptRXase3EL9t5sYT2DC8Us9gfVqOXY= +maunium.net/go/mautrix v0.24.1-0.20250605042548-d228995d718a/go.mod h1:HqA1HUutQYJkrYRPkK64itARDz79PCec1oWVEB72HVQ= diff --git a/pkg/connector/client.go b/pkg/connector/client.go index 300bbec..3d3ba5b 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -78,6 +78,7 @@ func (wa *WhatsAppConnector) LoadUserLogin(ctx context.Context, login *bridgev2. w.Client.AutomaticMessageRerequestFromPhone = true w.Client.GetMessageForRetry = w.trackNotFoundRetry w.Client.PreRetryCallback = w.trackFoundRetry + w.Client.BackgroundEventCtx = wa.Bridge.BackgroundCtx w.Client.SetForceActiveDeliveryReceipts(wa.Config.ForceActiveDeliveryReceipts) w.Client.InitialAutoReconnect = wa.Config.InitialAutoReconnect } else { From 07b52859df45b309cde78dc80b1eb0ace0d5c292 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 5 Jun 2025 16:45:25 +0300 Subject: [PATCH 020/276] 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 8ea054c..b52bca3 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.8.7 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250605112842-73d070ff1c78 + go.mau.fi/whatsmeow v0.0.0-20250605134436-66acc15b3df4 golang.org/x/image v0.27.0 golang.org/x/net v0.40.0 golang.org/x/sync v0.14.0 diff --git a/go.sum b/go.sum index c444ef4..dd65d5e 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ go.mau.fi/util v0.8.7 h1:ywKarPxouJQEEijTs4mPlxC7F4AWEKokEpWc+2TYy6c= go.mau.fi/util v0.8.7/go.mod h1:j6R3cENakc1f8HpQeFl0N15UiSTcNmIfDBNJUbL71RY= 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-20250605112842-73d070ff1c78 h1:LGAZWk6ofDh6SrkVX/y/yJHGAv8ThHJTh0WFfHMZ1oI= -go.mau.fi/whatsmeow v0.0.0-20250605112842-73d070ff1c78/go.mod h1:Qy3L3BNBcnxfrAQ09lmFMa0ItZfg8zl9DzxKrptzfU4= +go.mau.fi/whatsmeow v0.0.0-20250605134436-66acc15b3df4 h1:iFZQX++78gz6lGCOBHkkiMtpG1Ea7HLJZB53bF0UFKg= +go.mau.fi/whatsmeow v0.0.0-20250605134436-66acc15b3df4/go.mod h1:Qy3L3BNBcnxfrAQ09lmFMa0ItZfg8zl9DzxKrptzfU4= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= From 65af858748aa103ed0cd7f6346f95b2c2d6f95aa Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 6 Jun 2025 12:47:56 +0300 Subject: [PATCH 021/276] dependencies: update whatsmeow --- go.mod | 2 +- go.sum | 4 ++-- pkg/connector/client.go | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index b52bca3..9739ae0 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.8.7 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250605134436-66acc15b3df4 + go.mau.fi/whatsmeow v0.0.0-20250606094702-6110de3471ab golang.org/x/image v0.27.0 golang.org/x/net v0.40.0 golang.org/x/sync v0.14.0 diff --git a/go.sum b/go.sum index dd65d5e..535c8ef 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ go.mau.fi/util v0.8.7 h1:ywKarPxouJQEEijTs4mPlxC7F4AWEKokEpWc+2TYy6c= go.mau.fi/util v0.8.7/go.mod h1:j6R3cENakc1f8HpQeFl0N15UiSTcNmIfDBNJUbL71RY= 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-20250605134436-66acc15b3df4 h1:iFZQX++78gz6lGCOBHkkiMtpG1Ea7HLJZB53bF0UFKg= -go.mau.fi/whatsmeow v0.0.0-20250605134436-66acc15b3df4/go.mod h1:Qy3L3BNBcnxfrAQ09lmFMa0ItZfg8zl9DzxKrptzfU4= +go.mau.fi/whatsmeow v0.0.0-20250606094702-6110de3471ab h1:O4B+yX5a7vvp/PFONGRhiZqUTxTZ+F195j0zBdr8QBg= +go.mau.fi/whatsmeow v0.0.0-20250606094702-6110de3471ab/go.mod h1:Qy3L3BNBcnxfrAQ09lmFMa0ItZfg8zl9DzxKrptzfU4= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= diff --git a/pkg/connector/client.go b/pkg/connector/client.go index 3d3ba5b..161c247 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -75,6 +75,7 @@ func (wa *WhatsAppConnector) LoadUserLogin(ctx context.Context, login *bridgev2. w.Client.EnableDecryptedEventBuffer = true w.Client.ManualHistorySyncDownload = true } + w.Client.SendReportingTokens = true w.Client.AutomaticMessageRerequestFromPhone = true w.Client.GetMessageForRetry = w.trackNotFoundRetry w.Client.PreRetryCallback = w.trackFoundRetry From d528ea3e13373ed154c2a64b0149b336d4ddaa3a Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 6 Jun 2025 16:51:55 +0300 Subject: [PATCH 022/276] 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 9739ae0..207dd64 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.8.7 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250606094702-6110de3471ab + go.mau.fi/whatsmeow v0.0.0-20250606135134-9359be831c50 golang.org/x/image v0.27.0 golang.org/x/net v0.40.0 golang.org/x/sync v0.14.0 diff --git a/go.sum b/go.sum index 535c8ef..b8820bf 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ go.mau.fi/util v0.8.7 h1:ywKarPxouJQEEijTs4mPlxC7F4AWEKokEpWc+2TYy6c= go.mau.fi/util v0.8.7/go.mod h1:j6R3cENakc1f8HpQeFl0N15UiSTcNmIfDBNJUbL71RY= 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-20250606094702-6110de3471ab h1:O4B+yX5a7vvp/PFONGRhiZqUTxTZ+F195j0zBdr8QBg= -go.mau.fi/whatsmeow v0.0.0-20250606094702-6110de3471ab/go.mod h1:Qy3L3BNBcnxfrAQ09lmFMa0ItZfg8zl9DzxKrptzfU4= +go.mau.fi/whatsmeow v0.0.0-20250606135134-9359be831c50 h1:sd55J2a0u28g8znMzFbyVUTThXdPncjztO/BWg1QCoo= +go.mau.fi/whatsmeow v0.0.0-20250606135134-9359be831c50/go.mod h1:Qy3L3BNBcnxfrAQ09lmFMa0ItZfg8zl9DzxKrptzfU4= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= From d6c6b09d166da2e0ce308c803dcf597234530575 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 6 Jun 2025 19:50:35 +0300 Subject: [PATCH 023/276] dependencies: update whatsmeow for event context cancel fix --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 207dd64..02bb856 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.8.7 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250606135134-9359be831c50 + go.mau.fi/whatsmeow v0.0.0-20250606163700-6e87b20af9a9 golang.org/x/image v0.27.0 golang.org/x/net v0.40.0 golang.org/x/sync v0.14.0 diff --git a/go.sum b/go.sum index b8820bf..33220bc 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ go.mau.fi/util v0.8.7 h1:ywKarPxouJQEEijTs4mPlxC7F4AWEKokEpWc+2TYy6c= go.mau.fi/util v0.8.7/go.mod h1:j6R3cENakc1f8HpQeFl0N15UiSTcNmIfDBNJUbL71RY= 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-20250606135134-9359be831c50 h1:sd55J2a0u28g8znMzFbyVUTThXdPncjztO/BWg1QCoo= -go.mau.fi/whatsmeow v0.0.0-20250606135134-9359be831c50/go.mod h1:Qy3L3BNBcnxfrAQ09lmFMa0ItZfg8zl9DzxKrptzfU4= +go.mau.fi/whatsmeow v0.0.0-20250606163700-6e87b20af9a9 h1:lYf5nA3VZouNbl3kG9iCnYEqCCbJcOQzzgo4K6eEcG4= +go.mau.fi/whatsmeow v0.0.0-20250606163700-6e87b20af9a9/go.mod h1:Qy3L3BNBcnxfrAQ09lmFMa0ItZfg8zl9DzxKrptzfU4= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= From cf526768278e6af4c0c23cef5fbd2b17cb5ad3fe Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 6 Jun 2025 20:01:29 +0300 Subject: [PATCH 024/276] dependencies: update whatsmeow for more context fixes --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 02bb856..5372e66 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.8.7 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250606163700-6e87b20af9a9 + go.mau.fi/whatsmeow v0.0.0-20250606170101-3afe34f8ab8f golang.org/x/image v0.27.0 golang.org/x/net v0.40.0 golang.org/x/sync v0.14.0 diff --git a/go.sum b/go.sum index 33220bc..bdd1c00 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ go.mau.fi/util v0.8.7 h1:ywKarPxouJQEEijTs4mPlxC7F4AWEKokEpWc+2TYy6c= go.mau.fi/util v0.8.7/go.mod h1:j6R3cENakc1f8HpQeFl0N15UiSTcNmIfDBNJUbL71RY= 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-20250606163700-6e87b20af9a9 h1:lYf5nA3VZouNbl3kG9iCnYEqCCbJcOQzzgo4K6eEcG4= -go.mau.fi/whatsmeow v0.0.0-20250606163700-6e87b20af9a9/go.mod h1:Qy3L3BNBcnxfrAQ09lmFMa0ItZfg8zl9DzxKrptzfU4= +go.mau.fi/whatsmeow v0.0.0-20250606170101-3afe34f8ab8f h1:8csRM0kOS9nGgT162JFwi3FZ93NPM6fWVf/d5AfTceA= +go.mau.fi/whatsmeow v0.0.0-20250606170101-3afe34f8ab8f/go.mod h1:Qy3L3BNBcnxfrAQ09lmFMa0ItZfg8zl9DzxKrptzfU4= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= From 7b01903ca1487aa870da57093456434fcbc43c87 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 9 Jun 2025 18:53:23 +0530 Subject: [PATCH 025/276] handlewhatsapp: sync member list on first lid message (#799) --- go.mod | 2 +- go.sum | 4 ++-- pkg/connector/chatinfo.go | 13 +++++++++++++ pkg/connector/events.go | 28 ++++++++++++++++++++++++++++ pkg/waid/dbmeta.go | 1 + 5 files changed, 45 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 5372e66..31d0808 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( golang.org/x/sync v0.14.0 google.golang.org/protobuf v1.36.6 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.24.1-0.20250605042548-d228995d718a + maunium.net/go/mautrix v0.24.1-0.20250609115446-a154718b5d4c ) require ( diff --git a/go.sum b/go.sum index bdd1c00..226d3ee 100644 --- a/go.sum +++ b/go.sum @@ -101,5 +101,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.24.1-0.20250605042548-d228995d718a h1:XsaGeWjlp2WJVptRXase3EL9t5sYT2DC8Us9gfVqOXY= -maunium.net/go/mautrix v0.24.1-0.20250605042548-d228995d718a/go.mod h1:HqA1HUutQYJkrYRPkK64itARDz79PCec1oWVEB72HVQ= +maunium.net/go/mautrix v0.24.1-0.20250609115446-a154718b5d4c h1:VLAz8J3UU6AS8OaPpiwrp1NvhgjJOWBbbQ8Y8oh7Cdg= +maunium.net/go/mautrix v0.24.1-0.20250609115446-a154718b5d4c/go.mod h1:HqA1HUutQYJkrYRPkK64itARDz79PCec1oWVEB72HVQ= diff --git a/pkg/connector/chatinfo.go b/pkg/connector/chatinfo.go index af75af3..4d06ccc 100644 --- a/pkg/connector/chatinfo.go +++ b/pkg/connector/chatinfo.go @@ -272,7 +272,20 @@ func (wa *WhatsAppClient) wrapGroupInfo(info *types.GroupInfo) *bridgev2.ChatInf } else { member.PowerLevel = ptr.Ptr(defaultPL) } + member.MemberEventExtra = map[string]any{ + "com.beeper.exclude_from_timeline": true, + } wrapped.Members.MemberMap[waid.MakeUserID(pcp.JID)] = member + if pcp.JID.Server == types.HiddenUserServer && !pcp.PhoneNumber.IsEmpty() { + wrapped.Members.MemberMap[waid.MakeUserID(pcp.PhoneNumber)] = bridgev2.ChatMember{ + EventSender: wa.makeEventSender(pcp.PhoneNumber), + Membership: event.MembershipLeave, + PrevMembership: event.MembershipJoin, + MemberEventExtra: map[string]any{ + "com.beeper.exclude_from_timeline": true, + }, + } + } } if !info.LinkedParentJID.IsEmpty() { diff --git a/pkg/connector/events.go b/pkg/connector/events.go index 7f5435b..da3ec54 100644 --- a/pkg/connector/events.go +++ b/pkg/connector/events.go @@ -124,6 +124,34 @@ func (evt *WAMessageEvent) AddLogContext(c zerolog.Context) zerolog.Context { return evt.MessageInfoWrapper.AddLogContext(c).Str("parsed_message_type", evt.parsedMessageType) } +func (evt *WAMessageEvent) PreHandle(ctx context.Context, portal *bridgev2.Portal) { + if evt.Info.Sender.Server != types.HiddenUserServer || evt.Info.Chat.Server != types.GroupServer { + return + } + portalJID, err := waid.ParsePortalID(portal.ID) + if err != nil { + return + } + meta := portal.Metadata.(*waid.PortalMetadata) + if meta.AddressingMode == types.AddressingModeLID || meta.LIDMigrationAttempted { + return + } + log := zerolog.Ctx(ctx) + meta.LIDMigrationAttempted = true + info, err := evt.wa.Client.GetGroupInfo(portalJID) + if err != nil { + log.Err(err).Msg("Failed to get group info for lid migration") + return + } + if info.AddressingMode != types.AddressingModeLID { + log.Warn().Msg("Received LID message, but group addressing mode isn't set to LID? Not migrating") + return + } + log.Info().Msg("Resyncing group members as it appears to have switched to LID addressing mode") + portal.UpdateInfo(ctx, evt.wa.wrapGroupInfo(info), evt.wa.UserLogin, nil, time.Time{}) + log.Debug().Msg("Finished resyncing after LID change") +} + func (evt *WAMessageEvent) PostHandle(ctx context.Context, portal *bridgev2.Portal) { if ph := evt.postHandle; ph != nil { evt.postHandle = nil diff --git a/pkg/waid/dbmeta.go b/pkg/waid/dbmeta.go index 65760d2..4e0cd66 100644 --- a/pkg/waid/dbmeta.go +++ b/pkg/waid/dbmeta.go @@ -109,6 +109,7 @@ type PortalMetadata struct { LastSync jsontime.Unix `json:"last_sync,omitempty"` CommunityAnnouncementGroup bool `json:"is_cag,omitempty"` AddressingMode types.AddressingMode `json:"addressing_mode,omitempty"` + LIDMigrationAttempted bool `json:"lid_migration_attempted,omitempty"` } type GhostMetadata struct { From 3dd3ed82d5f0e41154a824c5a44106d9abab1b8e Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 9 Jun 2025 17:05:03 +0300 Subject: [PATCH 026/276] client: update background context on connect --- pkg/connector/client.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/connector/client.go b/pkg/connector/client.go index 161c247..d75a7fd 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -191,6 +191,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 if err := wa.Client.Connect(); err != nil { state := status.BridgeState{ StateEvent: status.StateUnknownError, @@ -223,6 +224,7 @@ func (wa *WhatsAppClient) ConnectBackground(ctx context.Context, params *bridgev if wa.Client == nil { return bridgev2.ErrNotLoggedIn } + wa.Client.BackgroundEventCtx = wa.Main.Bridge.BackgroundCtx wa.offlineSyncWaiter = make(chan error) wa.Main.firstClientConnectOnce.Do(wa.Main.onFirstClientConnect) if err := wa.Main.updateProxy(ctx, wa.Client, false); err != nil { From 7933a1ff2365b1052c79a022e8d653bf5d9fe08a Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 9 Jun 2025 19:48:47 +0300 Subject: [PATCH 027/276] handlewhatsapp: improve lid migration detection --- go.mod | 2 +- go.sum | 4 ++-- pkg/connector/events.go | 3 ++- pkg/connector/userinfo.go | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 31d0808..9966011 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( golang.org/x/sync v0.14.0 google.golang.org/protobuf v1.36.6 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.24.1-0.20250609115446-a154718b5d4c + maunium.net/go/mautrix v0.24.1-0.20250609163037-72bacbb666fb ) require ( diff --git a/go.sum b/go.sum index 226d3ee..dcb40a8 100644 --- a/go.sum +++ b/go.sum @@ -101,5 +101,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.24.1-0.20250609115446-a154718b5d4c h1:VLAz8J3UU6AS8OaPpiwrp1NvhgjJOWBbbQ8Y8oh7Cdg= -maunium.net/go/mautrix v0.24.1-0.20250609115446-a154718b5d4c/go.mod h1:HqA1HUutQYJkrYRPkK64itARDz79PCec1oWVEB72HVQ= +maunium.net/go/mautrix v0.24.1-0.20250609163037-72bacbb666fb h1:Cq5ewnYg3tu5eRo4h9tsWI4YjALVgnARBVTebqYe5qw= +maunium.net/go/mautrix v0.24.1-0.20250609163037-72bacbb666fb/go.mod h1:HqA1HUutQYJkrYRPkK64itARDz79PCec1oWVEB72HVQ= diff --git a/pkg/connector/events.go b/pkg/connector/events.go index da3ec54..fbda2bc 100644 --- a/pkg/connector/events.go +++ b/pkg/connector/events.go @@ -125,7 +125,8 @@ func (evt *WAMessageEvent) AddLogContext(c zerolog.Context) zerolog.Context { } func (evt *WAMessageEvent) PreHandle(ctx context.Context, portal *bridgev2.Portal) { - if evt.Info.Sender.Server != types.HiddenUserServer || evt.Info.Chat.Server != types.GroupServer { + if (evt.Info.Sender.Server != types.HiddenUserServer && evt.Info.AddressingMode != types.AddressingModeLID) || + evt.Info.Chat.Server != types.GroupServer { return } portalJID, err := waid.ParsePortalID(portal.ID) diff --git a/pkg/connector/userinfo.go b/pkg/connector/userinfo.go index 1fed10f..848359c 100644 --- a/pkg/connector/userinfo.go +++ b/pkg/connector/userinfo.go @@ -161,7 +161,7 @@ func (wa *WhatsAppClient) doGhostResync(ctx context.Context, queue map[types.JID } func (wa *WhatsAppClient) GetUserInfo(ctx context.Context, ghost *bridgev2.Ghost) (*bridgev2.UserInfo, error) { - if ghost.Name != "" { + if ghost.Name != "" && ghost.NameSet { wa.EnqueueGhostResync(ghost) return nil, nil } From 9d1b49de7dd05f73342b7039205531c127ccf5ee Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 9 Jun 2025 19:53:23 +0300 Subject: [PATCH 028/276] handlewhatsapp: add log identifier for lid migration --- pkg/connector/events.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/connector/events.go b/pkg/connector/events.go index fbda2bc..a8ca798 100644 --- a/pkg/connector/events.go +++ b/pkg/connector/events.go @@ -137,7 +137,8 @@ func (evt *WAMessageEvent) PreHandle(ctx context.Context, portal *bridgev2.Porta if meta.AddressingMode == types.AddressingModeLID || meta.LIDMigrationAttempted { return } - log := zerolog.Ctx(ctx) + 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) if err != nil { From 5ba8c9e43d005f5ff234788260176744a480fb63 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 9 Jun 2025 22:27:50 +0300 Subject: [PATCH 029/276] handlewhatsapp: only consider addressing mode for migration check --- pkg/connector/events.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/connector/events.go b/pkg/connector/events.go index a8ca798..31a2a3b 100644 --- a/pkg/connector/events.go +++ b/pkg/connector/events.go @@ -125,8 +125,7 @@ func (evt *WAMessageEvent) AddLogContext(c zerolog.Context) zerolog.Context { } func (evt *WAMessageEvent) PreHandle(ctx context.Context, portal *bridgev2.Portal) { - if (evt.Info.Sender.Server != types.HiddenUserServer && evt.Info.AddressingMode != types.AddressingModeLID) || - evt.Info.Chat.Server != types.GroupServer { + if evt.Info.AddressingMode != types.AddressingModeLID || evt.Info.Chat.Server != types.GroupServer { return } portalJID, err := waid.ParsePortalID(portal.ID) From 053c4a6a766ea89ceeea3a927415b7596a672f79 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 10 Jun 2025 16:40:41 +0300 Subject: [PATCH 030/276] handlewhatsapp: force lid DMs into phone number portal --- pkg/connector/handlewhatsapp.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index 8df3bcc..8a1480d 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -256,6 +256,15 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) { } func (wa *WhatsAppClient) handleWAMessage(ctx context.Context, evt *events.Message) { + if evt.Info.Chat.Server == types.HiddenUserServer && evt.Info.Sender.ToNonAD() == evt.Info.Chat && evt.Info.SenderAlt.Server == types.DefaultUserServer { + wa.UserLogin.Log.Debug(). + Stringer("lid", evt.Info.Sender). + Stringer("pn", evt.Info.SenderAlt). + Str("message_id", evt.Info.ID). + Msg("Forced LID DM sender to phone number in incoming message") + evt.Info.Sender, evt.Info.SenderAlt = evt.Info.SenderAlt, evt.Info.Sender + evt.Info.Chat = evt.Info.Sender.ToNonAD() + } wa.UserLogin.Log.Trace(). Any("info", evt.Info). Any("payload", evt.Message). @@ -326,6 +335,15 @@ func (wa *WhatsAppClient) handleWAUndecryptableMessage(evt *events.Undecryptable } func (wa *WhatsAppClient) handleWAReceipt(evt *events.Receipt) { + 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() + } if evt.IsFromMe && evt.Sender.Device == 0 { wa.phoneSeen(evt.Timestamp) } From 107ccf36448bc57bd4643c70255bf6c811648627 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 11 Jun 2025 16:09:45 +0300 Subject: [PATCH 031/276] dependencies: update --- go.mod | 14 +++++++------- go.sum | 28 ++++++++++++++-------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/go.mod b/go.mod index 9966011..8ee6a93 100644 --- a/go.mod +++ b/go.mod @@ -11,13 +11,13 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.8.7 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250606170101-3afe34f8ab8f - golang.org/x/image v0.27.0 - golang.org/x/net v0.40.0 - golang.org/x/sync v0.14.0 + go.mau.fi/whatsmeow v0.0.0-20250611130243-afe87b6dd8b4 + golang.org/x/image v0.28.0 + golang.org/x/net v0.41.0 + golang.org/x/sync v0.15.0 google.golang.org/protobuf v1.36.6 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.24.1-0.20250609163037-72bacbb666fb + maunium.net/go/mautrix v0.24.1-0.20250611123434-15d0b63eb6ab ) require ( @@ -39,10 +39,10 @@ require ( github.com/yuin/goldmark v1.7.11 // indirect go.mau.fi/libsignal v0.2.0 // indirect go.mau.fi/zeroconfig v0.1.3 // indirect - golang.org/x/crypto v0.38.0 // indirect + golang.org/x/crypto v0.39.0 // indirect golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect golang.org/x/sys v0.33.0 // indirect - golang.org/x/text v0.25.0 // indirect + golang.org/x/text v0.26.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 dcb40a8..c053b02 100644 --- a/go.sum +++ b/go.sum @@ -69,27 +69,27 @@ go.mau.fi/util v0.8.7 h1:ywKarPxouJQEEijTs4mPlxC7F4AWEKokEpWc+2TYy6c= go.mau.fi/util v0.8.7/go.mod h1:j6R3cENakc1f8HpQeFl0N15UiSTcNmIfDBNJUbL71RY= 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-20250606170101-3afe34f8ab8f h1:8csRM0kOS9nGgT162JFwi3FZ93NPM6fWVf/d5AfTceA= -go.mau.fi/whatsmeow v0.0.0-20250606170101-3afe34f8ab8f/go.mod h1:Qy3L3BNBcnxfrAQ09lmFMa0ItZfg8zl9DzxKrptzfU4= +go.mau.fi/whatsmeow v0.0.0-20250611130243-afe87b6dd8b4 h1:z8sNDR8Ix933MBrihNrfo9eauhQw26D6gBEPpk5KXL8= +go.mau.fi/whatsmeow v0.0.0-20250611130243-afe87b6dd8b4/go.mod h1:Qy3L3BNBcnxfrAQ09lmFMa0ItZfg8zl9DzxKrptzfU4= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= -golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= -golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= +golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= +golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI= golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= -golang.org/x/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w= -golang.org/x/image v0.27.0/go.mod h1:xbdrClrAUway1MUTEZDq9mz/UpRwYAkFFNUslZtcB+g= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/image v0.28.0 h1:gdem5JW1OLS4FbkWgLO+7ZeFzYtL3xClb97GaUzYMFE= +golang.org/x/image v0.28.0/go.mod h1:GUJYXtnGKEUgggyzh+Vxt+AviiCcyiwpsl8iQ8MvwGY= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 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.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -101,5 +101,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.24.1-0.20250609163037-72bacbb666fb h1:Cq5ewnYg3tu5eRo4h9tsWI4YjALVgnARBVTebqYe5qw= -maunium.net/go/mautrix v0.24.1-0.20250609163037-72bacbb666fb/go.mod h1:HqA1HUutQYJkrYRPkK64itARDz79PCec1oWVEB72HVQ= +maunium.net/go/mautrix v0.24.1-0.20250611123434-15d0b63eb6ab h1:2oysPkLLkQkLhtH29C+hOXFxRU486dLQCmngTFw7oAA= +maunium.net/go/mautrix v0.24.1-0.20250611123434-15d0b63eb6ab/go.mod h1:HqA1HUutQYJkrYRPkK64itARDz79PCec1oWVEB72HVQ= From bbbfe4f8464267d890f38a4305b4f6089c170fef Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 11 Jun 2025 16:36:41 +0300 Subject: [PATCH 032/276] docker: update to Alpine 3.22 --- Dockerfile | 4 ++-- Dockerfile.ci | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 34fba37..32d7a6d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1-alpine3.21 AS builder +FROM golang:1-alpine3.22 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.21 +FROM alpine:3.22 ENV UID=1337 \ GID=1337 diff --git a/Dockerfile.ci b/Dockerfile.ci index e8e79e1..28c7cfb 100644 --- a/Dockerfile.ci +++ b/Dockerfile.ci @@ -1,4 +1,4 @@ -FROM alpine:3.21 +FROM alpine:3.22 ENV UID=1337 \ GID=1337 From c72dde9ab506fe177c3dbbd13f89a551c8c74bb4 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 11 Jun 2025 17:45:07 +0300 Subject: [PATCH 033/276] 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 8ee6a93..6f463f4 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.8.7 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250611130243-afe87b6dd8b4 + go.mau.fi/whatsmeow v0.0.0-20250611144432-af1a723d11ee golang.org/x/image v0.28.0 golang.org/x/net v0.41.0 golang.org/x/sync v0.15.0 diff --git a/go.sum b/go.sum index c053b02..5347d51 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ go.mau.fi/util v0.8.7 h1:ywKarPxouJQEEijTs4mPlxC7F4AWEKokEpWc+2TYy6c= go.mau.fi/util v0.8.7/go.mod h1:j6R3cENakc1f8HpQeFl0N15UiSTcNmIfDBNJUbL71RY= 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-20250611130243-afe87b6dd8b4 h1:z8sNDR8Ix933MBrihNrfo9eauhQw26D6gBEPpk5KXL8= -go.mau.fi/whatsmeow v0.0.0-20250611130243-afe87b6dd8b4/go.mod h1:Qy3L3BNBcnxfrAQ09lmFMa0ItZfg8zl9DzxKrptzfU4= +go.mau.fi/whatsmeow v0.0.0-20250611144432-af1a723d11ee h1:mnM1j3XNOVkE/yepWa2ym5dLdhhEV7FWADx7I/vvkis= +go.mau.fi/whatsmeow v0.0.0-20250611144432-af1a723d11ee/go.mod h1:Qy3L3BNBcnxfrAQ09lmFMa0ItZfg8zl9DzxKrptzfU4= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= From ed5deee13c4578092a1cf71429d50695cf6a81bd Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sat, 14 Jun 2025 12:31:57 +0300 Subject: [PATCH 034/276] dependencies: update --- go.mod | 12 ++++++------ go.sum | 20 ++++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/go.mod b/go.mod index 6f463f4..84d094a 100644 --- a/go.mod +++ b/go.mod @@ -2,22 +2,22 @@ module go.mau.fi/mautrix-whatsapp go 1.23.0 -toolchain go1.24.3 +toolchain go1.24.4 require ( github.com/gorilla/mux v1.8.0 github.com/gorilla/websocket v1.5.0 github.com/lib/pq v1.10.9 github.com/rs/zerolog v1.34.0 - go.mau.fi/util v0.8.7 + go.mau.fi/util v0.8.8-0.20250612103042-2aa072eb60f0 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250611144432-af1a723d11ee + go.mau.fi/whatsmeow v0.0.0-20250612124742-cc05d16b464b golang.org/x/image v0.28.0 golang.org/x/net v0.41.0 golang.org/x/sync v0.15.0 google.golang.org/protobuf v1.36.6 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.24.1-0.20250611123434-15d0b63eb6ab + maunium.net/go/mautrix v0.24.1-0.20250614093103-c836dbafdfd9 ) require ( @@ -36,11 +36,11 @@ require ( github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/sjson v1.2.5 // indirect - github.com/yuin/goldmark v1.7.11 // indirect + github.com/yuin/goldmark v1.7.12 // indirect go.mau.fi/libsignal v0.2.0 // indirect go.mau.fi/zeroconfig v0.1.3 // indirect golang.org/x/crypto v0.39.0 // indirect - golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect + golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476 // indirect golang.org/x/sys v0.33.0 // indirect golang.org/x/text v0.26.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect diff --git a/go.sum b/go.sum index 5347d51..d39c070 100644 --- a/go.sum +++ b/go.sum @@ -61,22 +61,22 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= -github.com/yuin/goldmark v1.7.11 h1:ZCxLyDMtz0nT2HFfsYG8WZ47Trip2+JyLysKcMYE5bo= -github.com/yuin/goldmark v1.7.11/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= +github.com/yuin/goldmark v1.7.12 h1:YwGP/rrea2/CnCtUHgjuolG/PnMxdQtPMO5PvaE2/nY= +github.com/yuin/goldmark v1.7.12/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.8.7 h1:ywKarPxouJQEEijTs4mPlxC7F4AWEKokEpWc+2TYy6c= -go.mau.fi/util v0.8.7/go.mod h1:j6R3cENakc1f8HpQeFl0N15UiSTcNmIfDBNJUbL71RY= +go.mau.fi/util v0.8.8-0.20250612103042-2aa072eb60f0 h1:EcDJfYWX6aVT3c6nWTg9Qly41rNKabzzERt7OFzVerA= +go.mau.fi/util v0.8.8-0.20250612103042-2aa072eb60f0/go.mod h1:Y/kS3loxTEhy8Vill513EtPXr+CRDdae+Xj2BXXMy/c= 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-20250611144432-af1a723d11ee h1:mnM1j3XNOVkE/yepWa2ym5dLdhhEV7FWADx7I/vvkis= -go.mau.fi/whatsmeow v0.0.0-20250611144432-af1a723d11ee/go.mod h1:Qy3L3BNBcnxfrAQ09lmFMa0ItZfg8zl9DzxKrptzfU4= +go.mau.fi/whatsmeow v0.0.0-20250612124742-cc05d16b464b h1:jiEaJcX+R5bzfdpNIyAPzp5BgvdAjs2L7GEmjH98qm4= +go.mau.fi/whatsmeow v0.0.0-20250612124742-cc05d16b464b/go.mod h1:Qy3L3BNBcnxfrAQ09lmFMa0ItZfg8zl9DzxKrptzfU4= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= -golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI= -golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= +golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476 h1:bsqhLWFR6G6xiQcb+JoGqdKdRU6WzPWmK8E0jxTjzo4= +golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= golang.org/x/image v0.28.0 h1:gdem5JW1OLS4FbkWgLO+7ZeFzYtL3xClb97GaUzYMFE= golang.org/x/image v0.28.0/go.mod h1:GUJYXtnGKEUgggyzh+Vxt+AviiCcyiwpsl8iQ8MvwGY= golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= @@ -101,5 +101,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.24.1-0.20250611123434-15d0b63eb6ab h1:2oysPkLLkQkLhtH29C+hOXFxRU486dLQCmngTFw7oAA= -maunium.net/go/mautrix v0.24.1-0.20250611123434-15d0b63eb6ab/go.mod h1:HqA1HUutQYJkrYRPkK64itARDz79PCec1oWVEB72HVQ= +maunium.net/go/mautrix v0.24.1-0.20250614093103-c836dbafdfd9 h1:4wv3FArgoOG2Ro+pYUdQdtBo+25TdzIGwtL44DFbSbY= +maunium.net/go/mautrix v0.24.1-0.20250614093103-c836dbafdfd9/go.mod h1:IKIE5XbiJ38HbWzHcq9Bo3LYTiKARiEFQfzlZYmi5KE= From d58b0267f3af033b424219ec8f00d85594365e57 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sat, 14 Jun 2025 12:36:01 +0300 Subject: [PATCH 035/276] startchat: don't allow creating DMs with LIDs for now --- pkg/connector/startchat.go | 43 ++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/pkg/connector/startchat.go b/pkg/connector/startchat.go index e2631bc..7443ed3 100644 --- a/pkg/connector/startchat.go +++ b/pkg/connector/startchat.go @@ -53,7 +53,7 @@ func looksEmaily(str string) bool { } func (wa *WhatsAppClient) validateIdentifer(number string) (types.JID, error) { - if strings.HasSuffix(number, "@"+types.BotServer) { + if strings.HasSuffix(number, "@"+types.BotServer) || strings.HasSuffix(number, "@"+types.HiddenUserServer) { return types.ParseJID(number) } if strings.HasSuffix(number, "@"+types.DefaultUserServer) { @@ -96,12 +96,47 @@ 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) + if err != nil { + return jid, fmt.Errorf("failed to get phone number for lid: %w", err) + } else if pn.IsEmpty() { + // Don't allow starting chats with LIDs for now + return jid, fmt.Errorf("phone number not found") + } + return pn, nil + } + return jid, nil +} + +func (wa *WhatsAppClient) makeCreateChatResponse(jid, origJID types.JID) *bridgev2.CreateChatResponse { + var redirID networkid.UserID + if origJID != jid { + redirID = waid.MakeUserID(jid) + } + return &bridgev2.CreateChatResponse{ + PortalKey: wa.makeWAPortalKey(jid), + PortalInfo: wa.wrapDMInfo(jid), + DMRedirectedTo: redirID, + } +} + func (wa *WhatsAppClient) CreateChatWithGhost(ctx context.Context, ghost *bridgev2.Ghost) (*bridgev2.CreateChatResponse, error) { - return &bridgev2.CreateChatResponse{PortalKey: wa.makeWAPortalKey(waid.ParseUserID(ghost.ID))}, nil + origJID := waid.ParseUserID(ghost.ID) + jid, err := wa.startChatLIDToPN(ctx, origJID) + if err != nil { + return nil, err + } + return wa.makeCreateChatResponse(jid, origJID), nil } func (wa *WhatsAppClient) ResolveIdentifier(ctx context.Context, identifier string, startChat bool) (*bridgev2.ResolveIdentifierResponse, error) { - jid, err := wa.validateIdentifer(identifier) + origJID, err := wa.validateIdentifer(identifier) + if err != nil { + return nil, err + } + jid, err := wa.startChatLIDToPN(ctx, origJID) if err != nil { return nil, err } @@ -113,7 +148,7 @@ func (wa *WhatsAppClient) ResolveIdentifier(ctx context.Context, identifier stri return &bridgev2.ResolveIdentifierResponse{ Ghost: ghost, UserID: waid.MakeUserID(jid), - Chat: &bridgev2.CreateChatResponse{PortalKey: wa.makeWAPortalKey(jid)}, + Chat: wa.makeCreateChatResponse(jid, origJID), }, nil } From 27045b27c6aa71e9b49f0ce564823e61f758006e Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sat, 14 Jun 2025 12:36:25 +0300 Subject: [PATCH 036/276] handlewhatsapp: redirect LID DMs sent from other devices to PN portal --- pkg/connector/handlewhatsapp.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index 8a1480d..8caddc1 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -264,6 +264,13 @@ func (wa *WhatsAppClient) handleWAMessage(ctx context.Context, evt *events.Messa Msg("Forced LID DM sender to phone number in incoming message") evt.Info.Sender, evt.Info.SenderAlt = evt.Info.SenderAlt, evt.Info.Sender evt.Info.Chat = evt.Info.Sender.ToNonAD() + } else if evt.Info.Chat.Server == types.HiddenUserServer && evt.Info.IsFromMe && evt.Info.RecipientAlt.Server == types.DefaultUserServer { + wa.UserLogin.Log.Debug(). + Stringer("lid", evt.Info.Chat). + Stringer("pn", evt.Info.RecipientAlt). + Str("message_id", evt.Info.ID). + Msg("Forced LID DM sender to phone number in own message sent from another device") + evt.Info.Chat = evt.Info.RecipientAlt.ToNonAD() } wa.UserLogin.Log.Trace(). Any("info", evt.Info). @@ -343,6 +350,13 @@ func (wa *WhatsAppClient) handleWAReceipt(evt *events.Receipt) { 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() } if evt.IsFromMe && evt.Sender.Device == 0 { wa.phoneSeen(evt.Timestamp) From 3697418d32280273c5814ea562c274f0bbd8fc9d Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sat, 14 Jun 2025 16:02:40 +0300 Subject: [PATCH 037/276] client: log connect errors --- go.mod | 2 +- go.sum | 4 ++-- pkg/connector/client.go | 4 ++++ pkg/connector/example-config.yaml | 2 ++ 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 84d094a..82743d6 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.8.8-0.20250612103042-2aa072eb60f0 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250612124742-cc05d16b464b + go.mau.fi/whatsmeow v0.0.0-20250614130217-d5152ad30f01 golang.org/x/image v0.28.0 golang.org/x/net v0.41.0 golang.org/x/sync v0.15.0 diff --git a/go.sum b/go.sum index d39c070..e28a1fa 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ go.mau.fi/util v0.8.8-0.20250612103042-2aa072eb60f0 h1:EcDJfYWX6aVT3c6nWTg9Qly41 go.mau.fi/util v0.8.8-0.20250612103042-2aa072eb60f0/go.mod h1:Y/kS3loxTEhy8Vill513EtPXr+CRDdae+Xj2BXXMy/c= 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-20250612124742-cc05d16b464b h1:jiEaJcX+R5bzfdpNIyAPzp5BgvdAjs2L7GEmjH98qm4= -go.mau.fi/whatsmeow v0.0.0-20250612124742-cc05d16b464b/go.mod h1:Qy3L3BNBcnxfrAQ09lmFMa0ItZfg8zl9DzxKrptzfU4= +go.mau.fi/whatsmeow v0.0.0-20250614130217-d5152ad30f01 h1:sB4V19Cvf68lQTrCTmpf3AxUyV9Vl/MTR/zs92z1kpk= +go.mau.fi/whatsmeow v0.0.0-20250614130217-d5152ad30f01/go.mod h1:Qy3L3BNBcnxfrAQ09lmFMa0ItZfg8zl9DzxKrptzfU4= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= diff --git a/pkg/connector/client.go b/pkg/connector/client.go index d75a7fd..543c7d4 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -193,9 +193,13 @@ func (wa *WhatsAppClient) Connect(ctx context.Context) { wa.startLoops() wa.Client.BackgroundEventCtx = 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{ StateEvent: status.StateUnknownError, Error: WAConnectionFailed, + Info: map[string]any{ + "go_error": err.Error(), + }, } wa.UserLogin.BridgeState.Send(state) } diff --git a/pkg/connector/example-config.yaml b/pkg/connector/example-config.yaml index eb5fb2d..37c41f7 100644 --- a/pkg/connector/example-config.yaml +++ b/pkg/connector/example-config.yaml @@ -61,6 +61,8 @@ force_active_delivery_receipts: false # When direct media is enabled and a piece of media isn't available on the WhatsApp servers, # should it be automatically requested from the phone? direct_media_auto_request: true +# Should the bridge automatically reconnect if it fails to connect on startup? +initial_auto_reconnect: true # Settings for converting animated stickers. animated_sticker: From c298436511786b896486ddf9b947b1a41fa22c70 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 16 Jun 2025 18:17:00 +0300 Subject: [PATCH 038/276] Bump version to v0.12.2 --- CHANGELOG.md | 13 +++++++++++++ cmd/mautrix-whatsapp/main.go | 2 +- go.mod | 6 +++--- go.sum | 12 ++++++------ 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2f2485..17d1e72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +# v0.12.2 (2025-06-16) + +* Improved support for `@lid` users. + * **N.B.** As mentioned in the v0.12.0 release, old registration files may + have `[0-9]+` in the `users` regex. You must change it to `.+`, as the new + `lid` identifiers are bridged as `lid-` instead of just ``. +* Updated Docker image to Alpine 3.22. +* Fixed network errors on first connect not triggering automatic reconnect. +* Fixed animated sticker zips not being extracted when using direct media. + # v0.12.1 (2025-05-16) * Added prefix to identify forwarded messages on WhatsApp. @@ -13,6 +23,9 @@ * Migrated Signal session store to use new `@lid` identifiers to support future chats that don't expose phone numbers. + * **N.B.** Old registration files may have `[0-9]+` in the `users` regex. You + must change it to `.+`, as the new `lid` identifiers are bridged as + `lid-` instead of just ``. * Added fallbacks for various business message types. * Added support for bridging invites, kicks and leaves in groups. * Re-added `invite-link`, `join` and `sync` commands for groups. diff --git a/cmd/mautrix-whatsapp/main.go b/cmd/mautrix-whatsapp/main.go index a0542f2..40170f5 100644 --- a/cmd/mautrix-whatsapp/main.go +++ b/cmd/mautrix-whatsapp/main.go @@ -23,7 +23,7 @@ var m = mxmain.BridgeMain{ Name: "mautrix-whatsapp", URL: "https://github.com/mautrix/whatsapp", Description: "A Matrix-WhatsApp puppeting bridge.", - Version: "0.12.1", + Version: "0.12.2", Connector: c, } diff --git a/go.mod b/go.mod index 82743d6..b64022a 100644 --- a/go.mod +++ b/go.mod @@ -9,15 +9,15 @@ require ( github.com/gorilla/websocket v1.5.0 github.com/lib/pq v1.10.9 github.com/rs/zerolog v1.34.0 - go.mau.fi/util v0.8.8-0.20250612103042-2aa072eb60f0 + go.mau.fi/util v0.8.8 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250614130217-d5152ad30f01 + go.mau.fi/whatsmeow v0.0.0-20250616150942-80f2a1807103 golang.org/x/image v0.28.0 golang.org/x/net v0.41.0 golang.org/x/sync v0.15.0 google.golang.org/protobuf v1.36.6 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.24.1-0.20250614093103-c836dbafdfd9 + maunium.net/go/mautrix v0.24.1 ) require ( diff --git a/go.sum b/go.sum index e28a1fa..5b0554f 100644 --- a/go.sum +++ b/go.sum @@ -65,12 +65,12 @@ github.com/yuin/goldmark v1.7.12 h1:YwGP/rrea2/CnCtUHgjuolG/PnMxdQtPMO5PvaE2/nY= github.com/yuin/goldmark v1.7.12/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.8.8-0.20250612103042-2aa072eb60f0 h1:EcDJfYWX6aVT3c6nWTg9Qly41rNKabzzERt7OFzVerA= -go.mau.fi/util v0.8.8-0.20250612103042-2aa072eb60f0/go.mod h1:Y/kS3loxTEhy8Vill513EtPXr+CRDdae+Xj2BXXMy/c= +go.mau.fi/util v0.8.8 h1:OnuEEc/sIJFhnq4kFggiImUpcmnmL/xpvQMRu5Fiy5c= +go.mau.fi/util v0.8.8/go.mod h1:Y/kS3loxTEhy8Vill513EtPXr+CRDdae+Xj2BXXMy/c= 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-20250614130217-d5152ad30f01 h1:sB4V19Cvf68lQTrCTmpf3AxUyV9Vl/MTR/zs92z1kpk= -go.mau.fi/whatsmeow v0.0.0-20250614130217-d5152ad30f01/go.mod h1:Qy3L3BNBcnxfrAQ09lmFMa0ItZfg8zl9DzxKrptzfU4= +go.mau.fi/whatsmeow v0.0.0-20250616150942-80f2a1807103 h1:9X0vTlbEPIa9X7Vr/dszlJnKdGjCXftmasc81qo7Wxk= +go.mau.fi/whatsmeow v0.0.0-20250616150942-80f2a1807103/go.mod h1:bEyyFvXlwr/18B2pOkdX1vWAx1+y1NJX+sCXVyw01UA= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= @@ -101,5 +101,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.24.1-0.20250614093103-c836dbafdfd9 h1:4wv3FArgoOG2Ro+pYUdQdtBo+25TdzIGwtL44DFbSbY= -maunium.net/go/mautrix v0.24.1-0.20250614093103-c836dbafdfd9/go.mod h1:IKIE5XbiJ38HbWzHcq9Bo3LYTiKARiEFQfzlZYmi5KE= +maunium.net/go/mautrix v0.24.1 h1:09/xi4qTeA03g1n/DPmmqAlT8Cx4QrgwiPlmLVzA9AU= +maunium.net/go/mautrix v0.24.1/go.mod h1:Xy6o+pXmbqmgWsUWh15EQ1eozjC+k/VT/7kloByv9PI= From 5f3abdba539f0ac6802a95f0fb9c25a764cc87e0 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 16 Jun 2025 21:37:22 +0300 Subject: [PATCH 039/276] handlewhatsapp: fix read receipts in lid groups --- pkg/connector/handlewhatsapp.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index 8caddc1..375b31d 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -376,6 +376,11 @@ func (wa *WhatsAppClient) handleWAReceipt(evt *events.Receipt) { messageSender := wa.JID if !evt.MessageSender.IsEmpty() { messageSender = evt.MessageSender + } else if evt.Chat.Server == types.GroupServer && evt.Sender.Server == types.HiddenUserServer { + lid := wa.Device.GetLID() + if !lid.IsEmpty() { + messageSender = lid + } } for i, id := range evt.MessageIDs { targets[i] = waid.MakeMessageID(evt.Chat, messageSender, id) From 628902dae83e4af21107a8eb97419317ce05907d Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 17 Jun 2025 22:24:37 +0530 Subject: [PATCH 040/276] handlewhatsapp: plumb event handling result to whatsmeow (#804) --- go.mod | 4 ++-- go.sum | 8 ++++---- pkg/connector/chatinfo.go | 5 ++++- pkg/connector/client.go | 2 +- pkg/connector/handlewhatsapp.go | 31 ++++++++++++++++++------------- 5 files changed, 29 insertions(+), 21 deletions(-) diff --git a/go.mod b/go.mod index b64022a..0593f4d 100644 --- a/go.mod +++ b/go.mod @@ -11,13 +11,13 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.8.8 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250616150942-80f2a1807103 + go.mau.fi/whatsmeow v0.0.0-20250617164104-eea582eed117 golang.org/x/image v0.28.0 golang.org/x/net v0.41.0 golang.org/x/sync v0.15.0 google.golang.org/protobuf v1.36.6 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.24.1 + maunium.net/go/mautrix v0.24.2-0.20250617163829-26da46dbbf6e ) require ( diff --git a/go.sum b/go.sum index 5b0554f..764c395 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ go.mau.fi/util v0.8.8 h1:OnuEEc/sIJFhnq4kFggiImUpcmnmL/xpvQMRu5Fiy5c= go.mau.fi/util v0.8.8/go.mod h1:Y/kS3loxTEhy8Vill513EtPXr+CRDdae+Xj2BXXMy/c= 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-20250616150942-80f2a1807103 h1:9X0vTlbEPIa9X7Vr/dszlJnKdGjCXftmasc81qo7Wxk= -go.mau.fi/whatsmeow v0.0.0-20250616150942-80f2a1807103/go.mod h1:bEyyFvXlwr/18B2pOkdX1vWAx1+y1NJX+sCXVyw01UA= +go.mau.fi/whatsmeow v0.0.0-20250617164104-eea582eed117 h1:DJLAjmXGaEzLvkOb/Kb0Cg21KG1xBdq5lNJg2gkTOPE= +go.mau.fi/whatsmeow v0.0.0-20250617164104-eea582eed117/go.mod h1:bEyyFvXlwr/18B2pOkdX1vWAx1+y1NJX+sCXVyw01UA= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= @@ -101,5 +101,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.24.1 h1:09/xi4qTeA03g1n/DPmmqAlT8Cx4QrgwiPlmLVzA9AU= -maunium.net/go/mautrix v0.24.1/go.mod h1:Xy6o+pXmbqmgWsUWh15EQ1eozjC+k/VT/7kloByv9PI= +maunium.net/go/mautrix v0.24.2-0.20250617163829-26da46dbbf6e h1:Y8kbRpPcKMZn2gIjUFd15xzMB5GJ2bS6ZcOfvlx4KnE= +maunium.net/go/mautrix v0.24.2-0.20250617163829-26da46dbbf6e/go.mod h1:Xy6o+pXmbqmgWsUWh15EQ1eozjC+k/VT/7kloByv9PI= diff --git a/pkg/connector/chatinfo.go b/pkg/connector/chatinfo.go index 4d06ccc..4ee0a1b 100644 --- a/pkg/connector/chatinfo.go +++ b/pkg/connector/chatinfo.go @@ -427,7 +427,10 @@ func (wa *WhatsAppClient) makePortalAvatarFetcher(avatarID string, sender types. if !sender.IsEmpty() { evtSender = wa.makeEventSender(sender) } - senderIntent := portal.GetIntentFor(ctx, evtSender, wa.UserLogin, bridgev2.RemoteEventChatInfoChange) + senderIntent, ok := portal.GetIntentFor(ctx, evtSender, wa.UserLogin, bridgev2.RemoteEventChatInfoChange) + if !ok { + return false + } //lint:ignore SA1019 TODO invent a cleaner way to fetch avatar metadata before updating? return portal.Internal().UpdateAvatar(ctx, wrappedAvatar, senderIntent, ts) } diff --git a/pkg/connector/client.go b/pkg/connector/client.go index 543c7d4..3cc5633 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -69,7 +69,7 @@ func (wa *WhatsAppConnector) LoadUserLogin(ctx context.Context, login *bridgev2. if w.Device != nil { log := w.UserLogin.Log.With().Str("component", "whatsmeow").Logger() w.Client = whatsmeow.NewClient(w.Device, waLog.Zerolog(log)) - w.Client.AddEventHandler(w.handleWAEvent) + w.Client.AddEventHandlerWithSuccessStatus(w.handleWAEvent) if bridgev2.PortalEventBuffer == 0 { w.Client.SynchronousAck = true w.Client.EnableDecryptedEventBuffer = true diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index 375b31d..3a9d5f2 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -71,19 +71,19 @@ func init() { }) } -func (wa *WhatsAppClient) handleWAEvent(rawEvt any) { +func (wa *WhatsAppClient) handleWAEvent(rawEvt any) (success bool) { log := wa.UserLogin.Log ctx := log.WithContext(wa.Main.Bridge.BackgroundCtx) switch evt := rawEvt.(type) { case *events.Message: - wa.handleWAMessage(ctx, evt) + return wa.handleWAMessage(ctx, evt) case *events.Receipt: - wa.handleWAReceipt(evt) + return wa.handleWAReceipt(evt) case *events.ChatPresence: wa.handleWAChatPresence(evt) case *events.UndecryptableMessage: - wa.handleWAUndecryptableMessage(evt) + return wa.handleWAUndecryptableMessage(evt) case *events.CallOffer: wa.handleWACallStart(evt.CallCreator, evt.CallID, "", evt.Timestamp) @@ -253,9 +253,11 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) { default: log.Debug().Type("event_type", rawEvt).Msg("Unhandled WhatsApp event") } + return true } -func (wa *WhatsAppClient) handleWAMessage(ctx context.Context, evt *events.Message) { +func (wa *WhatsAppClient) handleWAMessage(ctx context.Context, evt *events.Message) (success bool) { + success = true if evt.Info.Chat.Server == types.HiddenUserServer && evt.Info.Sender.ToNonAD() == evt.Info.Chat && evt.Info.SenderAlt.Server == types.DefaultUserServer { wa.UserLogin.Log.Debug(). Stringer("lid", evt.Info.Sender). @@ -307,7 +309,7 @@ func (wa *WhatsAppClient) handleWAMessage(ctx context.Context, evt *events.Messa evt.Message = decrypted } } - wa.Main.Bridge.QueueRemoteEvent(wa.UserLogin, &WAMessageEvent{ + res := wa.Main.Bridge.QueueRemoteEvent(wa.UserLogin, &WAMessageEvent{ MessageInfoWrapper: &MessageInfoWrapper{ Info: evt.Info, wa: wa, @@ -317,9 +319,10 @@ func (wa *WhatsAppClient) handleWAMessage(ctx context.Context, evt *events.Messa parsedMessageType: parsedMessageType, }) + return res.Success } -func (wa *WhatsAppClient) handleWAUndecryptableMessage(evt *events.UndecryptableMessage) { +func (wa *WhatsAppClient) handleWAUndecryptableMessage(evt *events.UndecryptableMessage) bool { wa.UserLogin.Log.Debug(). Any("info", evt.Info). Bool("unavailable", evt.IsUnavailable). @@ -327,21 +330,22 @@ func (wa *WhatsAppClient) handleWAUndecryptableMessage(evt *events.Undecryptable Msg("Received undecryptable WhatsApp message") wa.trackUndecryptable(evt) if evt.DecryptFailMode == events.DecryptFailHide { - return + return true } if evt.Info.Chat == types.StatusBroadcastJID && !wa.Main.Config.EnableStatusBroadcast { - return + return true } - wa.Main.Bridge.QueueRemoteEvent(wa.UserLogin, &WAUndecryptableMessage{ + res := wa.Main.Bridge.QueueRemoteEvent(wa.UserLogin, &WAUndecryptableMessage{ MessageInfoWrapper: &MessageInfoWrapper{ Info: evt.Info, wa: wa, }, Type: evt.UnavailableType, }) + return res.Success } -func (wa *WhatsAppClient) handleWAReceipt(evt *events.Receipt) { +func (wa *WhatsAppClient) handleWAReceipt(evt *events.Receipt) (success bool) { if evt.Chat.Server == types.HiddenUserServer && evt.Sender.ToNonAD() == evt.Chat && evt.SenderAlt.Server == types.DefaultUserServer { wa.UserLogin.Log.Debug(). Stringer("lid", evt.Sender). @@ -370,7 +374,7 @@ func (wa *WhatsAppClient) handleWAReceipt(evt *events.Receipt) { case types.ReceiptTypeSender: fallthrough default: - return + return true } targets := make([]networkid.MessageID, len(evt.MessageIDs)) messageSender := wa.JID @@ -385,7 +389,7 @@ func (wa *WhatsAppClient) handleWAReceipt(evt *events.Receipt) { for i, id := range evt.MessageIDs { targets[i] = waid.MakeMessageID(evt.Chat, messageSender, id) } - wa.Main.Bridge.QueueRemoteEvent(wa.UserLogin, &simplevent.Receipt{ + res := wa.Main.Bridge.QueueRemoteEvent(wa.UserLogin, &simplevent.Receipt{ EventMeta: simplevent.EventMeta{ Type: evtType, PortalKey: wa.makeWAPortalKey(evt.Chat), @@ -394,6 +398,7 @@ func (wa *WhatsAppClient) handleWAReceipt(evt *events.Receipt) { }, Targets: targets, }) + return res.Success } func (wa *WhatsAppClient) handleWAChatPresence(evt *events.ChatPresence) { From ab48b4123f4569b7f9824ec8e406c4a69556ad6c Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 17 Jun 2025 20:05:38 +0300 Subject: [PATCH 041/276] 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 0593f4d..a8be453 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.8.8 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250617164104-eea582eed117 + go.mau.fi/whatsmeow v0.0.0-20250617170509-947866bb9f75 golang.org/x/image v0.28.0 golang.org/x/net v0.41.0 golang.org/x/sync v0.15.0 diff --git a/go.sum b/go.sum index 764c395..0e60811 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ go.mau.fi/util v0.8.8 h1:OnuEEc/sIJFhnq4kFggiImUpcmnmL/xpvQMRu5Fiy5c= go.mau.fi/util v0.8.8/go.mod h1:Y/kS3loxTEhy8Vill513EtPXr+CRDdae+Xj2BXXMy/c= 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-20250617164104-eea582eed117 h1:DJLAjmXGaEzLvkOb/Kb0Cg21KG1xBdq5lNJg2gkTOPE= -go.mau.fi/whatsmeow v0.0.0-20250617164104-eea582eed117/go.mod h1:bEyyFvXlwr/18B2pOkdX1vWAx1+y1NJX+sCXVyw01UA= +go.mau.fi/whatsmeow v0.0.0-20250617170509-947866bb9f75 h1:5SvY8TY8Yo0wXpn+enqE1sCQBKRSHxLcMrP+AG4Pe+k= +go.mau.fi/whatsmeow v0.0.0-20250617170509-947866bb9f75/go.mod h1:bEyyFvXlwr/18B2pOkdX1vWAx1+y1NJX+sCXVyw01UA= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= From ca37208de3324b9b0102defe4bf392a43bee2b97 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 27 Jun 2025 15:28:16 +0200 Subject: [PATCH 042/276] 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 a8be453..6a97684 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.8.8 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250617170509-947866bb9f75 + go.mau.fi/whatsmeow v0.0.0-20250627132727-b8996aaf77fc golang.org/x/image v0.28.0 golang.org/x/net v0.41.0 golang.org/x/sync v0.15.0 diff --git a/go.sum b/go.sum index 0e60811..9b74da3 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ go.mau.fi/util v0.8.8 h1:OnuEEc/sIJFhnq4kFggiImUpcmnmL/xpvQMRu5Fiy5c= go.mau.fi/util v0.8.8/go.mod h1:Y/kS3loxTEhy8Vill513EtPXr+CRDdae+Xj2BXXMy/c= 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-20250617170509-947866bb9f75 h1:5SvY8TY8Yo0wXpn+enqE1sCQBKRSHxLcMrP+AG4Pe+k= -go.mau.fi/whatsmeow v0.0.0-20250617170509-947866bb9f75/go.mod h1:bEyyFvXlwr/18B2pOkdX1vWAx1+y1NJX+sCXVyw01UA= +go.mau.fi/whatsmeow v0.0.0-20250627132727-b8996aaf77fc h1:82eP5GFAu/4Kxt4OrTZ+UH+NB/W4Wsy6VmW9dTkWI/g= +go.mau.fi/whatsmeow v0.0.0-20250627132727-b8996aaf77fc/go.mod h1:bEyyFvXlwr/18B2pOkdX1vWAx1+y1NJX+sCXVyw01UA= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= From 4a27484cbed94b21ba13eeee811add704cbf657d Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 27 Jun 2025 15:33:53 +0200 Subject: [PATCH 043/276] 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 6a97684..087ac5b 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.8.8 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250627132727-b8996aaf77fc + go.mau.fi/whatsmeow v0.0.0-20250627133320-9948ada1f8aa golang.org/x/image v0.28.0 golang.org/x/net v0.41.0 golang.org/x/sync v0.15.0 diff --git a/go.sum b/go.sum index 9b74da3..16bf155 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ go.mau.fi/util v0.8.8 h1:OnuEEc/sIJFhnq4kFggiImUpcmnmL/xpvQMRu5Fiy5c= go.mau.fi/util v0.8.8/go.mod h1:Y/kS3loxTEhy8Vill513EtPXr+CRDdae+Xj2BXXMy/c= 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-20250627132727-b8996aaf77fc h1:82eP5GFAu/4Kxt4OrTZ+UH+NB/W4Wsy6VmW9dTkWI/g= -go.mau.fi/whatsmeow v0.0.0-20250627132727-b8996aaf77fc/go.mod h1:bEyyFvXlwr/18B2pOkdX1vWAx1+y1NJX+sCXVyw01UA= +go.mau.fi/whatsmeow v0.0.0-20250627133320-9948ada1f8aa h1:iptlIdHZm4vv1np6iZ3+tV4XdADVWUUcX3zc18NhXmU= +go.mau.fi/whatsmeow v0.0.0-20250627133320-9948ada1f8aa/go.mod h1:bEyyFvXlwr/18B2pOkdX1vWAx1+y1NJX+sCXVyw01UA= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= From da130190f0cfb76b0a71ed539cfa759f2935b641 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 30 Jun 2025 19:52:05 +0300 Subject: [PATCH 044/276] chatinfo: fix phone number removes in group lid migration --- pkg/connector/chatinfo.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/connector/chatinfo.go b/pkg/connector/chatinfo.go index 4ee0a1b..1c984c0 100644 --- a/pkg/connector/chatinfo.go +++ b/pkg/connector/chatinfo.go @@ -278,7 +278,7 @@ func (wa *WhatsAppClient) wrapGroupInfo(info *types.GroupInfo) *bridgev2.ChatInf wrapped.Members.MemberMap[waid.MakeUserID(pcp.JID)] = member if pcp.JID.Server == types.HiddenUserServer && !pcp.PhoneNumber.IsEmpty() { wrapped.Members.MemberMap[waid.MakeUserID(pcp.PhoneNumber)] = bridgev2.ChatMember{ - EventSender: wa.makeEventSender(pcp.PhoneNumber), + EventSender: bridgev2.EventSender{Sender: waid.MakeUserID(pcp.PhoneNumber)}, Membership: event.MembershipLeave, PrevMembership: event.MembershipJoin, MemberEventExtra: map[string]any{ From 58cc5110d2932f1cdee42975847e069e60dae72f Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 1 Jul 2025 01:23:56 +0300 Subject: [PATCH 045/276] client: don't fetch latest version number for background connections --- pkg/connector/client.go | 2 +- pkg/connector/connector.go | 29 ++++++++++++++++++++++++++--- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/pkg/connector/client.go b/pkg/connector/client.go index 3cc5633..cfd6374 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -230,7 +230,7 @@ func (wa *WhatsAppClient) ConnectBackground(ctx context.Context, params *bridgev } wa.Client.BackgroundEventCtx = wa.Main.Bridge.BackgroundCtx wa.offlineSyncWaiter = make(chan error) - wa.Main.firstClientConnectOnce.Do(wa.Main.onFirstClientConnect) + wa.Main.backgroundConnectOnce.Do(wa.Main.onFirstBackgroundConnect) if err := wa.Main.updateProxy(ctx, wa.Client, false); err != nil { zerolog.Ctx(ctx).Err(err).Msg("Failed to update proxy") } diff --git a/pkg/connector/connector.go b/pkg/connector/connector.go index ca8cd5b..1e41e80 100644 --- a/pkg/connector/connector.go +++ b/pkg/connector/connector.go @@ -51,6 +51,7 @@ type WhatsAppConnector struct { DB *wadb.Database firstClientConnectOnce sync.Once + backgroundConnectOnce sync.Once mediaEditCache MediaEditCache mediaEditCacheLock sync.RWMutex @@ -146,13 +147,34 @@ func (wa *WhatsAppConnector) Start(ctx context.Context) error { } func (wa *WhatsAppConnector) Stop() { - if stop := wa.stopMediaEditCacheLoop.Load(); stop != nil { + if stop := wa.stopMediaEditCacheLoop.Swap(nil); stop != nil { (*stop)() } } +const kvWAVersion = "whatsapp_web_version" + +func (wa *WhatsAppConnector) onFirstBackgroundConnect() { + verStr := wa.Bridge.DB.KV.Get(wa.Bridge.BackgroundCtx, kvWAVersion) + if verStr == "" { + wa.Bridge.Log.Warn().Msg("No WhatsApp web version number cached in database") + return + } + ver, err := store.ParseVersion(verStr) + if err != nil { + wa.Bridge.Log.Err(err).Msg("Failed to parse WhatsApp web version number from database") + return + } + wa.Bridge.Log.Debug(). + Stringer("hardcoded_version", store.GetWAVersion()). + Stringer("cached_version", ver). + Msg("Using cached WhatsApp web version number") + store.SetWAVersion(ver) +} + func (wa *WhatsAppConnector) onFirstClientConnect() { - ver, err := whatsmeow.GetLatestVersion(wa.Bridge.BackgroundCtx, nil) + ctx := wa.Bridge.BackgroundCtx + ver, err := whatsmeow.GetLatestVersion(ctx, nil) if err != nil { wa.Bridge.Log.Err(err).Msg("Failed to get latest WhatsApp web version number") } else { @@ -161,8 +183,9 @@ func (wa *WhatsAppConnector) onFirstClientConnect() { Stringer("latest_version", *ver). Msg("Got latest WhatsApp web version number") store.SetWAVersion(*ver) + wa.Bridge.DB.KV.Set(ctx, kvWAVersion, ver.String()) } - meclCtx, cancel := context.WithCancel(context.Background()) + meclCtx, cancel := context.WithCancel(ctx) wa.stopMediaEditCacheLoop.Store(&cancel) go wa.mediaEditCacheExpireLoop(meclCtx) } From 216fe786aeceb1e6b4d938a81a53f7f04bd888cc Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 1 Jul 2025 01:24:44 +0300 Subject: [PATCH 046/276] 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 087ac5b..7d10d2f 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.8.8 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250627133320-9948ada1f8aa + go.mau.fi/whatsmeow v0.0.0-20250630101711-a0eb38c885c5 golang.org/x/image v0.28.0 golang.org/x/net v0.41.0 golang.org/x/sync v0.15.0 diff --git a/go.sum b/go.sum index 16bf155..f24e145 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ go.mau.fi/util v0.8.8 h1:OnuEEc/sIJFhnq4kFggiImUpcmnmL/xpvQMRu5Fiy5c= go.mau.fi/util v0.8.8/go.mod h1:Y/kS3loxTEhy8Vill513EtPXr+CRDdae+Xj2BXXMy/c= 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-20250627133320-9948ada1f8aa h1:iptlIdHZm4vv1np6iZ3+tV4XdADVWUUcX3zc18NhXmU= -go.mau.fi/whatsmeow v0.0.0-20250627133320-9948ada1f8aa/go.mod h1:bEyyFvXlwr/18B2pOkdX1vWAx1+y1NJX+sCXVyw01UA= +go.mau.fi/whatsmeow v0.0.0-20250630101711-a0eb38c885c5 h1:e6TbliVnWP/FUoMwpkzT2lBzINogtoK0VR2dI0iUl1M= +go.mau.fi/whatsmeow v0.0.0-20250630101711-a0eb38c885c5/go.mod h1:bEyyFvXlwr/18B2pOkdX1vWAx1+y1NJX+sCXVyw01UA= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= From c7cb936e068194d1f0eb9c024dc01da7862bdf0d Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 1 Jul 2025 15:08:56 +0300 Subject: [PATCH 047/276] 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 7d10d2f..754f9c9 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.8.8 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250630101711-a0eb38c885c5 + go.mau.fi/whatsmeow v0.0.0-20250701120710-561c998da20e golang.org/x/image v0.28.0 golang.org/x/net v0.41.0 golang.org/x/sync v0.15.0 diff --git a/go.sum b/go.sum index f24e145..7ca686a 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ go.mau.fi/util v0.8.8 h1:OnuEEc/sIJFhnq4kFggiImUpcmnmL/xpvQMRu5Fiy5c= go.mau.fi/util v0.8.8/go.mod h1:Y/kS3loxTEhy8Vill513EtPXr+CRDdae+Xj2BXXMy/c= 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-20250630101711-a0eb38c885c5 h1:e6TbliVnWP/FUoMwpkzT2lBzINogtoK0VR2dI0iUl1M= -go.mau.fi/whatsmeow v0.0.0-20250630101711-a0eb38c885c5/go.mod h1:bEyyFvXlwr/18B2pOkdX1vWAx1+y1NJX+sCXVyw01UA= +go.mau.fi/whatsmeow v0.0.0-20250701120710-561c998da20e h1:BHka19pqaymXLg9WpgWBidQNC+bGJrzQWg5pBHe748w= +go.mau.fi/whatsmeow v0.0.0-20250701120710-561c998da20e/go.mod h1:bEyyFvXlwr/18B2pOkdX1vWAx1+y1NJX+sCXVyw01UA= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= From 2f90bd4a0c6cd5a058481229be57488da0dcf602 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 2 Jul 2025 21:01:23 +0300 Subject: [PATCH 048/276] directmedia: fix handling retries when there's no state --- pkg/connector/directmedia.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/connector/directmedia.go b/pkg/connector/directmedia.go index 29c4462..9a5eb86 100644 --- a/pkg/connector/directmedia.go +++ b/pkg/connector/directmedia.go @@ -225,7 +225,9 @@ func (wa *WhatsAppClient) receiveDirectMediaRetry(ctx context.Context, msg *data log.Warn().Err(err).Msg("Failed to decrypt media retry notification") return } - state.resultType = retryData.GetResult() + if state != nil { + state.resultType = retryData.GetResult() + } if retryData.GetResult() != waMmsRetry.MediaRetryNotification_SUCCESS { errorName := waMmsRetry.MediaRetryNotification_ResultType_name[int32(retryData.GetResult())] if retryData.GetDirectPath() == "" { From 4f6a3675a2f0592a7c12bbe1a1f609a163c46b1d Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 2 Jul 2025 21:12:24 +0300 Subject: [PATCH 049/276] 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 754f9c9..c6aa440 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.8.8 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250701120710-561c998da20e + go.mau.fi/whatsmeow v0.0.0-20250701221811-9adf672adc90 golang.org/x/image v0.28.0 golang.org/x/net v0.41.0 golang.org/x/sync v0.15.0 diff --git a/go.sum b/go.sum index 7ca686a..00109dc 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ go.mau.fi/util v0.8.8 h1:OnuEEc/sIJFhnq4kFggiImUpcmnmL/xpvQMRu5Fiy5c= go.mau.fi/util v0.8.8/go.mod h1:Y/kS3loxTEhy8Vill513EtPXr+CRDdae+Xj2BXXMy/c= 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-20250701120710-561c998da20e h1:BHka19pqaymXLg9WpgWBidQNC+bGJrzQWg5pBHe748w= -go.mau.fi/whatsmeow v0.0.0-20250701120710-561c998da20e/go.mod h1:bEyyFvXlwr/18B2pOkdX1vWAx1+y1NJX+sCXVyw01UA= +go.mau.fi/whatsmeow v0.0.0-20250701221811-9adf672adc90 h1:K9knC6oQMLPutKwDVnqdgrDEjMJ8D5nREE9Li3dH71k= +go.mau.fi/whatsmeow v0.0.0-20250701221811-9adf672adc90/go.mod h1:bEyyFvXlwr/18B2pOkdX1vWAx1+y1NJX+sCXVyw01UA= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= From d27e65510951d3b253ea3cc59aa84d221dd0246d Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 3 Jul 2025 00:54:33 +0300 Subject: [PATCH 050/276] msgconv/from-matrix: convert quicktime videos to mp4 when sending --- pkg/connector/capabilities.go | 11 ++++++----- pkg/msgconv/from-matrix.go | 10 +++++++--- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/pkg/connector/capabilities.go b/pkg/connector/capabilities.go index 1af3a9a..c54c378 100644 --- a/pkg/connector/capabilities.go +++ b/pkg/connector/capabilities.go @@ -23,7 +23,7 @@ func (wa *WhatsAppConnector) GetCapabilities() *bridgev2.NetworkGeneralCapabilit } func (wa *WhatsAppConnector) GetBridgeInfoVersion() (info, caps int) { - return 1, 1 + return 1, 2 } const WAMaxFileSize = 2000 * 1024 * 1024 @@ -38,7 +38,7 @@ func supportedIfFFmpeg() event.CapabilitySupportLevel { } func capID() string { - base := "fi.mau.whatsapp.capabilities.2025_01_10" + base := "fi.mau.whatsapp.capabilities.2025_06_03" if ffmpeg.Supported() { return base + "+ffmpeg" } @@ -116,9 +116,10 @@ var whatsappCaps = &event.RoomFeatures{ }, event.MsgVideo: { MimeTypes: map[string]event.CapabilitySupportLevel{ - "video/mp4": event.CapLevelFullySupported, - "video/3gpp": event.CapLevelFullySupported, - "video/webm": supportedIfFFmpeg(), + "video/mp4": event.CapLevelFullySupported, + "video/3gpp": event.CapLevelFullySupported, + "video/webm": supportedIfFFmpeg(), + "video/quicktime": supportedIfFFmpeg(), }, Caption: event.CapLevelFullySupported, MaxCaptionLength: MaxTextLength, diff --git a/pkg/msgconv/from-matrix.go b/pkg/msgconv/from-matrix.go index 3e82b06..105c1c2 100644 --- a/pkg/msgconv/from-matrix.go +++ b/pkg/msgconv/from-matrix.go @@ -464,13 +464,17 @@ func (mc *MessageConverter) reuploadFileToWhatsApp( switch mime { case "video/mp4", "video/3gpp": // allowed - case "video/webm": - data, err = ffmpeg.ConvertBytes(ctx, data, ".mp4", []string{"-f", "webm"}, []string{ + case "video/webm", "video/quicktime": + sourceFormat := "webm" + if mime == "video/quicktime" { + sourceFormat = "mov" + } + data, err = ffmpeg.ConvertBytes(ctx, data, ".mp4", []string{"-f", sourceFormat}, []string{ "-pix_fmt", "yuv420p", "-c:v", "libx264", "-filter:v", "crop='floor(in_w/2)*2:floor(in_h/2)*2'", }, mime) if err != nil { - return nil, nil, "video/webm", fmt.Errorf("%w (webm to mp4): %w", bridgev2.ErrMediaConvertFailed, err) + return nil, nil, mime, fmt.Errorf("%w (%s to mp4): %w", bridgev2.ErrMediaConvertFailed, sourceFormat, err) } mime = "video/mp4" case "image/gif": From a7e6fe39fb6531ff129487d60eeb67cd37590f4a Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 3 Jul 2025 21:18:13 +0300 Subject: [PATCH 051/276] msgconv/from-whatsapp: sync disappearing message timer from incoming messages even if it's 0 --- go.mod | 2 +- go.sum | 4 ++-- pkg/msgconv/from-whatsapp.go | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index c6aa440..e74c308 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( golang.org/x/sync v0.15.0 google.golang.org/protobuf v1.36.6 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.24.2-0.20250617163829-26da46dbbf6e + maunium.net/go/mautrix v0.24.2-0.20250703182219-b62535edaa57 ) require ( diff --git a/go.sum b/go.sum index 00109dc..9c6cce2 100644 --- a/go.sum +++ b/go.sum @@ -101,5 +101,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.24.2-0.20250617163829-26da46dbbf6e h1:Y8kbRpPcKMZn2gIjUFd15xzMB5GJ2bS6ZcOfvlx4KnE= -maunium.net/go/mautrix v0.24.2-0.20250617163829-26da46dbbf6e/go.mod h1:Xy6o+pXmbqmgWsUWh15EQ1eozjC+k/VT/7kloByv9PI= +maunium.net/go/mautrix v0.24.2-0.20250703182219-b62535edaa57 h1:z7/37EKswVuTiW26dF6lkPgf60JtKvxsOb0CfWrMeBI= +maunium.net/go/mautrix v0.24.2-0.20250703182219-b62535edaa57/go.mod h1:Xy6o+pXmbqmgWsUWh15EQ1eozjC+k/VT/7kloByv9PI= diff --git a/pkg/msgconv/from-whatsapp.go b/pkg/msgconv/from-whatsapp.go index 7ee11ee..11ef09a 100644 --- a/pkg/msgconv/from-whatsapp.go +++ b/pkg/msgconv/from-whatsapp.go @@ -225,9 +225,9 @@ func (mc *MessageConverter) ToMatrix( if contextInfo.GetExpiration() > 0 { cm.Disappear.Timer = time.Duration(contextInfo.GetExpiration()) * time.Second cm.Disappear.Type = database.DisappearingTypeAfterRead - if portal.Disappear.Timer != cm.Disappear.Timer && portal.Metadata.(*waid.PortalMetadata).DisappearingTimerSetAt < contextInfo.GetEphemeralSettingTimestamp() { - portal.UpdateDisappearingSetting(ctx, cm.Disappear, intent, info.Timestamp, true, true) - } + } + if portal.Disappear.Timer != cm.Disappear.Timer && portal.Metadata.(*waid.PortalMetadata).DisappearingTimerSetAt < contextInfo.GetEphemeralSettingTimestamp() { + portal.UpdateDisappearingSetting(ctx, cm.Disappear, intent, info.Timestamp, true, true) } if contextInfo.GetStanzaID() != "" { pcp, _ := types.ParseJID(contextInfo.GetParticipant()) From 3e96e7158fcf7a8d7193a8c5b7c610450c070fff Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 3 Jul 2025 21:44:15 +0300 Subject: [PATCH 052/276] id: fill sender login ID properly in LID event senders --- pkg/connector/backfill.go | 4 +-- pkg/connector/chatinfo.go | 38 ++++++++++----------- pkg/connector/commands.go | 2 +- pkg/connector/events.go | 6 ++-- pkg/connector/handlewhatsapp.go | 58 ++++++++++++++++----------------- pkg/connector/id.go | 22 +++++++++++-- pkg/connector/startchat.go | 8 ++--- 7 files changed, 78 insertions(+), 60 deletions(-) diff --git a/pkg/connector/backfill.go b/pkg/connector/backfill.go index 9a982ea..c0359cf 100644 --- a/pkg/connector/backfill.go +++ b/pkg/connector/backfill.go @@ -482,7 +482,7 @@ func (wa *WhatsAppClient) convertHistorySyncMessage( intent := wa.Main.Bridge.Bot wrapped := &bridgev2.BackfillMessage{ ConvertedMessage: wa.Main.MsgConv.ToMatrix(ctx, portal, wa.Client, intent, msg, info, isViewOnce, nil), - Sender: wa.makeEventSender(info.Sender), + Sender: wa.makeEventSender(ctx, info.Sender), ID: waid.MakeMessageID(info.Chat, info.Sender, info.ID), TxnID: networkid.TransactionID(waid.MakeMessageID(info.Chat, info.Sender, info.ID)), Timestamp: info.Timestamp, @@ -505,7 +505,7 @@ func (wa *WhatsAppClient) convertHistorySyncMessage( wrapped.Reactions[i] = &bridgev2.BackfillReaction{ TargetPart: ptr.Ptr(networkid.PartID("")), Timestamp: time.UnixMilli(reaction.GetSenderTimestampMS()), - Sender: wa.makeEventSender(sender), + Sender: wa.makeEventSender(ctx, sender), Emoji: reaction.GetText(), } } diff --git a/pkg/connector/chatinfo.go b/pkg/connector/chatinfo.go index 1c984c0..ef52e8d 100644 --- a/pkg/connector/chatinfo.go +++ b/pkg/connector/chatinfo.go @@ -32,10 +32,10 @@ func (wa *WhatsAppClient) GetChatInfo(ctx context.Context, portal *bridgev2.Port func (wa *WhatsAppClient) getChatInfo(ctx context.Context, portalJID types.JID, conv *wadb.Conversation) (wrapped *bridgev2.ChatInfo, err error) { switch portalJID.Server { case types.DefaultUserServer, types.HiddenUserServer, types.BotServer: - wrapped = wa.wrapDMInfo(portalJID) + wrapped = wa.wrapDMInfo(ctx, portalJID) case types.BroadcastServer: if portalJID == types.StatusBroadcastJID { - wrapped = wa.wrapStatusBroadcastInfo() + wrapped = wa.wrapStatusBroadcastInfo(ctx) } else { return nil, fmt.Errorf("broadcast list bridging is currently not supported") } @@ -44,14 +44,14 @@ func (wa *WhatsAppClient) getChatInfo(ctx context.Context, portalJID types.JID, if err != nil { return nil, err } - wrapped = wa.wrapGroupInfo(info) + wrapped = wa.wrapGroupInfo(ctx, info) wrapped.ExtraUpdates = bridgev2.MergeExtraUpdaters(wrapped.ExtraUpdates, updatePortalLastSyncAt) case types.NewsletterServer: info, err := wa.Client.GetNewsletterInfo(portalJID) if err != nil { return nil, err } - wrapped = wa.wrapNewsletterInfo(info) + wrapped = wa.wrapNewsletterInfo(ctx, info) default: return nil, fmt.Errorf("unsupported server %s", portalJID.Server) } @@ -138,7 +138,7 @@ const UnnamedBroadcastName = "Unnamed broadcast list" const PrivateChatTopic = "WhatsApp private chat" const BotChatTopic = "WhatsApp chat with a bot" -func (wa *WhatsAppClient) wrapDMInfo(jid types.JID) *bridgev2.ChatInfo { +func (wa *WhatsAppClient) wrapDMInfo(ctx context.Context, jid types.JID) *bridgev2.ChatInfo { info := &bridgev2.ChatInfo{ Topic: ptr.Ptr(PrivateChatTopic), Members: &bridgev2.ChatMemberList{ @@ -146,8 +146,8 @@ func (wa *WhatsAppClient) wrapDMInfo(jid types.JID) *bridgev2.ChatInfo { TotalMemberCount: 2, OtherUserID: waid.MakeUserID(jid), MemberMap: map[networkid.UserID]bridgev2.ChatMember{ - waid.MakeUserID(jid): {EventSender: wa.makeEventSender(jid)}, - waid.MakeUserID(wa.JID): {EventSender: wa.makeEventSender(wa.JID)}, + waid.MakeUserID(jid): {EventSender: wa.makeEventSender(ctx, jid)}, + waid.MakeUserID(wa.JID): {EventSender: wa.makeEventSender(ctx, wa.JID)}, }, PowerLevels: nil, }, @@ -166,7 +166,7 @@ func (wa *WhatsAppClient) wrapDMInfo(jid types.JID) *bridgev2.ChatInfo { return info } -func (wa *WhatsAppClient) wrapStatusBroadcastInfo() *bridgev2.ChatInfo { +func (wa *WhatsAppClient) wrapStatusBroadcastInfo(ctx context.Context) *bridgev2.ChatInfo { userLocal := &bridgev2.UserLocalPortalInfo{} if wa.Main.Config.MuteStatusBroadcast { userLocal.MutedUntil = ptr.Ptr(event.MutedForever) @@ -180,7 +180,7 @@ func (wa *WhatsAppClient) wrapStatusBroadcastInfo() *bridgev2.ChatInfo { Members: &bridgev2.ChatMemberList{ IsFull: false, MemberMap: map[networkid.UserID]bridgev2.ChatMember{ - waid.MakeUserID(wa.JID): {EventSender: wa.makeEventSender(wa.JID)}, + waid.MakeUserID(wa.JID): {EventSender: wa.makeEventSender(ctx, wa.JID)}, }, }, Type: ptr.Ptr(database.RoomTypeDefault), @@ -218,7 +218,7 @@ func setAddressingMode(mode types.AddressingMode) bridgev2.ExtraUpdater[*bridgev } } -func (wa *WhatsAppClient) wrapGroupInfo(info *types.GroupInfo) *bridgev2.ChatInfo { +func (wa *WhatsAppClient) wrapGroupInfo(ctx context.Context, info *types.GroupInfo) *bridgev2.ChatInfo { sendEventPL := defaultPL if info.IsAnnounce && !info.IsDefaultSubGroup { sendEventPL = adminPL @@ -262,7 +262,7 @@ func (wa *WhatsAppClient) wrapGroupInfo(info *types.GroupInfo) *bridgev2.ChatInf } for _, pcp := range info.Participants { member := bridgev2.ChatMember{ - EventSender: wa.makeEventSender(pcp.JID), + EventSender: wa.makeEventSender(ctx, pcp.JID), Membership: event.MembershipJoin, } if pcp.IsSuperAdmin { @@ -299,7 +299,7 @@ func (wa *WhatsAppClient) wrapGroupInfo(info *types.GroupInfo) *bridgev2.ChatInf return wrapped } -func (wa *WhatsAppClient) wrapGroupInfoChange(evt *events.GroupInfo) *bridgev2.ChatInfoChange { +func (wa *WhatsAppClient) wrapGroupInfoChange(ctx context.Context, evt *events.GroupInfo) *bridgev2.ChatInfoChange { var changes *bridgev2.ChatInfo if evt.Name != nil || evt.Topic != nil || evt.Ephemeral != nil || evt.Unlink != nil || evt.Link != nil { changes = &bridgev2.ChatInfo{} @@ -333,24 +333,24 @@ func (wa *WhatsAppClient) wrapGroupInfoChange(evt *events.GroupInfo) *bridgev2.C } for _, userID := range evt.Join { memberChanges.MemberMap[waid.MakeUserID(userID)] = bridgev2.ChatMember{ - EventSender: wa.makeEventSender(userID), + EventSender: wa.makeEventSender(ctx, userID), } } for _, userID := range evt.Promote { memberChanges.MemberMap[waid.MakeUserID(userID)] = bridgev2.ChatMember{ - EventSender: wa.makeEventSender(userID), + EventSender: wa.makeEventSender(ctx, userID), PowerLevel: ptr.Ptr(adminPL), } } for _, userID := range evt.Demote { memberChanges.MemberMap[waid.MakeUserID(userID)] = bridgev2.ChatMember{ - EventSender: wa.makeEventSender(userID), + EventSender: wa.makeEventSender(ctx, userID), PowerLevel: ptr.Ptr(defaultPL), } } for _, userID := range evt.Leave { memberChanges.MemberMap[waid.MakeUserID(userID)] = bridgev2.ChatMember{ - EventSender: wa.makeEventSender(userID), + EventSender: wa.makeEventSender(ctx, userID), Membership: event.MembershipLeave, } } @@ -425,7 +425,7 @@ func (wa *WhatsAppClient) makePortalAvatarFetcher(avatarID string, sender types. } var evtSender bridgev2.EventSender if !sender.IsEmpty() { - evtSender = wa.makeEventSender(sender) + evtSender = wa.makeEventSender(ctx, sender) } senderIntent, ok := portal.GetIntentFor(ctx, evtSender, wa.UserLogin, bridgev2.RemoteEventChatInfoChange) if !ok { @@ -436,7 +436,7 @@ func (wa *WhatsAppClient) makePortalAvatarFetcher(avatarID string, sender types. } } -func (wa *WhatsAppClient) wrapNewsletterInfo(info *types.NewsletterMetadata) *bridgev2.ChatInfo { +func (wa *WhatsAppClient) wrapNewsletterInfo(ctx context.Context, info *types.NewsletterMetadata) *bridgev2.ChatInfo { ownPowerLevel := defaultPL var mutedUntil *time.Time if info.ViewerMeta != nil { @@ -485,7 +485,7 @@ func (wa *WhatsAppClient) wrapNewsletterInfo(info *types.NewsletterMetadata) *br TotalMemberCount: info.ThreadMeta.SubscriberCount, MemberMap: map[networkid.UserID]bridgev2.ChatMember{ waid.MakeUserID(wa.JID): { - EventSender: wa.makeEventSender(wa.JID), + EventSender: wa.makeEventSender(ctx, wa.JID), PowerLevel: &ownPowerLevel, }, }, diff --git a/pkg/connector/commands.go b/pkg/connector/commands.go index 8e2ddf0..3017b15 100644 --- a/pkg/connector/commands.go +++ b/pkg/connector/commands.go @@ -120,7 +120,7 @@ func fnSync(ce *commands.Event) { return } for _, group := range groups { - wrapped := wa.wrapGroupInfo(group) + 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{ diff --git a/pkg/connector/events.go b/pkg/connector/events.go index 31a2a3b..035b2d5 100644 --- a/pkg/connector/events.go +++ b/pkg/connector/events.go @@ -73,7 +73,7 @@ func (evt *MessageInfoWrapper) GetTimestamp() time.Time { } func (evt *MessageInfoWrapper) GetSender() bridgev2.EventSender { - return evt.wa.makeEventSender(evt.Info.Sender) + return evt.wa.makeEventSender(evt.wa.Main.Bridge.BackgroundCtx, evt.Info.Sender) } func (evt *MessageInfoWrapper) GetID() networkid.MessageID { @@ -149,7 +149,7 @@ func (evt *WAMessageEvent) PreHandle(ctx context.Context, portal *bridgev2.Porta return } log.Info().Msg("Resyncing group members as it appears to have switched to LID addressing mode") - portal.UpdateInfo(ctx, evt.wa.wrapGroupInfo(info), evt.wa.UserLogin, nil, time.Time{}) + portal.UpdateInfo(ctx, evt.wa.wrapGroupInfo(ctx, info), evt.wa.UserLogin, nil, time.Time{}) log.Debug().Msg("Finished resyncing after LID change") } @@ -386,7 +386,7 @@ func (evt *WAMediaRetry) getRealSender() types.JID { } func (evt *WAMediaRetry) GetSender() bridgev2.EventSender { - return evt.wa.makeEventSender(evt.getRealSender()) + return evt.wa.makeEventSender(evt.wa.Main.Bridge.BackgroundCtx, evt.getRealSender()) } func (evt *WAMediaRetry) GetTargetMessage() networkid.MessageID { diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index 3a9d5f2..bd3f8e3 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -79,22 +79,22 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) (success bool) { case *events.Message: return wa.handleWAMessage(ctx, evt) case *events.Receipt: - return wa.handleWAReceipt(evt) + return wa.handleWAReceipt(ctx, evt) case *events.ChatPresence: - wa.handleWAChatPresence(evt) + wa.handleWAChatPresence(ctx, evt) case *events.UndecryptableMessage: return wa.handleWAUndecryptableMessage(evt) case *events.CallOffer: - wa.handleWACallStart(evt.CallCreator, evt.CallID, "", evt.Timestamp) + wa.handleWACallStart(ctx, evt.CallCreator, evt.CallID, "", evt.Timestamp) case *events.CallOfferNotice: - wa.handleWACallStart(evt.CallCreator, evt.CallID, evt.Type, evt.Timestamp) + wa.handleWACallStart(ctx, evt.CallCreator, evt.CallID, evt.Type, evt.Timestamp) case *events.CallTerminate, *events.CallRelayLatency, *events.CallAccept, *events.UnknownCallEvent: // ignore case *events.IdentityChange: - wa.handleWAIdentityChange(evt) + wa.handleWAIdentityChange(ctx, evt) case *events.MarkChatAsRead: - wa.handleWAMarkChatAsRead(evt) + wa.handleWAMarkChatAsRead(ctx, evt) case *events.DeleteForMe: wa.handleWADeleteForMe(evt) case *events.DeleteChat: @@ -115,15 +115,15 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) (success bool) { wa.UserLogin.QueueRemoteEvent(&WAMediaRetry{MediaRetry: evt, wa: wa}) case *events.GroupInfo: - wa.handleWAGroupInfoChange(evt) + wa.handleWAGroupInfoChange(ctx, evt) case *events.JoinedGroup: - wa.handleWAJoinedGroup(evt) + wa.handleWAJoinedGroup(ctx, evt) case *events.NewsletterJoin: - wa.handleWANewsletterJoin(evt) + wa.handleWANewsletterJoin(ctx, evt) case *events.NewsletterLeave: wa.handleWANewsletterLeave(evt) case *events.Picture: - go wa.handleWAPictureUpdate(evt) + go wa.handleWAPictureUpdate(ctx, evt) case *events.AppStateSyncComplete: if len(wa.GetStore().PushName) > 0 && evt.Name == appstate.WAPatchCriticalBlock { @@ -345,7 +345,7 @@ func (wa *WhatsAppClient) handleWAUndecryptableMessage(evt *events.Undecryptable return res.Success } -func (wa *WhatsAppClient) handleWAReceipt(evt *events.Receipt) (success bool) { +func (wa *WhatsAppClient) handleWAReceipt(ctx context.Context, evt *events.Receipt) (success bool) { if evt.Chat.Server == types.HiddenUserServer && evt.Sender.ToNonAD() == evt.Chat && evt.SenderAlt.Server == types.DefaultUserServer { wa.UserLogin.Log.Debug(). Stringer("lid", evt.Sender). @@ -393,7 +393,7 @@ func (wa *WhatsAppClient) handleWAReceipt(evt *events.Receipt) (success bool) { EventMeta: simplevent.EventMeta{ Type: evtType, PortalKey: wa.makeWAPortalKey(evt.Chat), - Sender: wa.makeEventSender(evt.Sender), + Sender: wa.makeEventSender(ctx, evt.Sender), Timestamp: evt.Timestamp, }, Targets: targets, @@ -401,7 +401,7 @@ func (wa *WhatsAppClient) handleWAReceipt(evt *events.Receipt) (success bool) { return res.Success } -func (wa *WhatsAppClient) handleWAChatPresence(evt *events.ChatPresence) { +func (wa *WhatsAppClient) handleWAChatPresence(ctx context.Context, evt *events.ChatPresence) { typingType := bridgev2.TypingTypeText timeout := 15 * time.Second if evt.Media == types.ChatPresenceMediaAudio { @@ -416,7 +416,7 @@ func (wa *WhatsAppClient) handleWAChatPresence(evt *events.ChatPresence) { Type: bridgev2.RemoteEventTyping, LogContext: nil, PortalKey: wa.makeWAPortalKey(evt.Chat), - Sender: wa.makeEventSender(evt.Sender), + Sender: wa.makeEventSender(ctx, evt.Sender), Timestamp: time.Now(), }, Timeout: timeout, @@ -443,7 +443,7 @@ func (wa *WhatsAppClient) handleWALogout(reason events.ConnectFailureReason, onC const callEventMaxAge = 15 * time.Minute -func (wa *WhatsAppClient) handleWACallStart(sender types.JID, id, callType string, ts time.Time) { +func (wa *WhatsAppClient) handleWACallStart(ctx context.Context, sender types.JID, id, callType string, ts time.Time) { if !wa.Main.Config.CallStartNotices || time.Since(ts) > callEventMaxAge { return } @@ -452,7 +452,7 @@ func (wa *WhatsAppClient) handleWACallStart(sender types.JID, id, callType strin Type: bridgev2.RemoteEventMessage, LogContext: nil, PortalKey: wa.makeWAPortalKey(sender), - Sender: wa.makeEventSender(sender), + Sender: wa.makeEventSender(ctx, sender), CreatePortal: true, Timestamp: ts, }, @@ -478,7 +478,7 @@ func convertCallStart(ctx context.Context, portal *bridgev2.Portal, intent bridg }, nil } -func (wa *WhatsAppClient) handleWAIdentityChange(evt *events.IdentityChange) { +func (wa *WhatsAppClient) handleWAIdentityChange(ctx context.Context, evt *events.IdentityChange) { if !wa.Main.Config.IdentityChangeNotices { return } @@ -487,7 +487,7 @@ func (wa *WhatsAppClient) handleWAIdentityChange(evt *events.IdentityChange) { Type: bridgev2.RemoteEventMessage, LogContext: nil, PortalKey: wa.makeWAPortalKey(evt.JID), - Sender: wa.makeEventSender(evt.JID), + Sender: wa.makeEventSender(ctx, evt.JID), CreatePortal: false, Timestamp: evt.Timestamp, }, @@ -540,12 +540,12 @@ func (wa *WhatsAppClient) handleWADeleteForMe(evt *events.DeleteForMe) { }) } -func (wa *WhatsAppClient) handleWAMarkChatAsRead(evt *events.MarkChatAsRead) { +func (wa *WhatsAppClient) handleWAMarkChatAsRead(ctx context.Context, evt *events.MarkChatAsRead) { wa.UserLogin.QueueRemoteEvent(&simplevent.Receipt{ EventMeta: simplevent.EventMeta{ Type: bridgev2.RemoteEventReadReceipt, PortalKey: wa.makeWAPortalKey(evt.JID), - Sender: wa.makeEventSender(wa.JID), + Sender: wa.makeEventSender(ctx, wa.JID), Timestamp: evt.Timestamp, }, ReadUpTo: evt.Timestamp, @@ -578,7 +578,7 @@ func (wa *WhatsAppClient) syncGhost(jid types.JID, reason string, pictureID *str go wa.syncRemoteProfile(ctx, ghost) } -func (wa *WhatsAppClient) handleWAPictureUpdate(evt *events.Picture) { +func (wa *WhatsAppClient) handleWAPictureUpdate(ctx context.Context, evt *events.Picture) { if evt.JID.Server == types.DefaultUserServer || evt.JID.Server == types.BotServer { wa.syncGhost(evt.JID, "picture event", &evt.PictureID) } else { @@ -599,7 +599,7 @@ func (wa *WhatsAppClient) handleWAPictureUpdate(evt *events.Picture) { Bool("remove_picture", evt.Remove) }, PortalKey: wa.makeWAPortalKey(evt.JID), - Sender: wa.makeEventSender(evt.Author), + Sender: wa.makeEventSender(ctx, evt.Author), Timestamp: evt.Timestamp, }, ChatInfoChange: &bridgev2.ChatInfoChange{ @@ -609,7 +609,7 @@ func (wa *WhatsAppClient) handleWAPictureUpdate(evt *events.Picture) { } } -func (wa *WhatsAppClient) handleWAGroupInfoChange(evt *events.GroupInfo) { +func (wa *WhatsAppClient) handleWAGroupInfoChange(ctx context.Context, evt *events.GroupInfo) { eventMeta := simplevent.EventMeta{ Type: bridgev2.RemoteEventChatInfoChange, LogContext: nil, @@ -618,7 +618,7 @@ func (wa *WhatsAppClient) handleWAGroupInfoChange(evt *events.GroupInfo) { Timestamp: evt.Timestamp, } if evt.Sender != nil { - eventMeta.Sender = wa.makeEventSender(*evt.Sender) + eventMeta.Sender = wa.makeEventSender(ctx, *evt.Sender) } if evt.Delete != nil { eventMeta.Type = bridgev2.RemoteEventChatDelete @@ -626,12 +626,12 @@ func (wa *WhatsAppClient) handleWAGroupInfoChange(evt *events.GroupInfo) { } else { wa.UserLogin.QueueRemoteEvent(&simplevent.ChatInfoChange{ EventMeta: eventMeta, - ChatInfoChange: wa.wrapGroupInfoChange(evt), + ChatInfoChange: wa.wrapGroupInfoChange(ctx, evt), }) } } -func (wa *WhatsAppClient) handleWAJoinedGroup(evt *events.JoinedGroup) { +func (wa *WhatsAppClient) handleWAJoinedGroup(ctx context.Context, evt *events.JoinedGroup) { wa.Main.Bridge.QueueRemoteEvent(wa.UserLogin, &simplevent.ChatResync{ EventMeta: simplevent.EventMeta{ Type: bridgev2.RemoteEventChatResync, @@ -639,11 +639,11 @@ func (wa *WhatsAppClient) handleWAJoinedGroup(evt *events.JoinedGroup) { PortalKey: wa.makeWAPortalKey(evt.JID), CreatePortal: true, }, - ChatInfo: wa.wrapGroupInfo(&evt.GroupInfo), + ChatInfo: wa.wrapGroupInfo(ctx, &evt.GroupInfo), }) } -func (wa *WhatsAppClient) handleWANewsletterJoin(evt *events.NewsletterJoin) { +func (wa *WhatsAppClient) handleWANewsletterJoin(ctx context.Context, evt *events.NewsletterJoin) { wa.Main.Bridge.QueueRemoteEvent(wa.UserLogin, &simplevent.ChatResync{ EventMeta: simplevent.EventMeta{ Type: bridgev2.RemoteEventChatResync, @@ -651,7 +651,7 @@ func (wa *WhatsAppClient) handleWANewsletterJoin(evt *events.NewsletterJoin) { PortalKey: wa.makeWAPortalKey(evt.ID), CreatePortal: true, }, - ChatInfo: wa.wrapNewsletterInfo(&evt.NewsletterMetadata), + ChatInfo: wa.wrapNewsletterInfo(ctx, &evt.NewsletterMetadata), }) } diff --git a/pkg/connector/id.go b/pkg/connector/id.go index ea4e4c8..e956e59 100644 --- a/pkg/connector/id.go +++ b/pkg/connector/id.go @@ -1,6 +1,9 @@ package connector import ( + "context" + + "github.com/rs/zerolog" "go.mau.fi/util/ptr" "go.mau.fi/whatsmeow/proto/waCommon" "go.mau.fi/whatsmeow/types" @@ -25,15 +28,30 @@ func (wa *WhatsAppClient) makeWAPortalKey(chatJID types.JID) networkid.PortalKey return key } -func (wa *WhatsAppClient) makeEventSender(id types.JID) bridgev2.EventSender { +func (wa *WhatsAppClient) makeEventSender(ctx context.Context, id types.JID) bridgev2.EventSender { if id.Server == types.NewsletterServer { // Send as bot return bridgev2.EventSender{} } + var senderLoginJID types.JID + if wa.Main.Bridge.Config.SplitPortals { + // no need for sender login ID + } else if id.Server == types.DefaultUserServer { + senderLoginJID = id + } else if id.Server == types.HiddenUserServer { + pn, err := wa.Device.LIDs.GetPNForLID(ctx, id) + if err != nil { + zerolog.Ctx(ctx).Err(err). + Stringer("lid", id). + Msg("Failed to get phone number for LID to make event sender") + } else if !pn.IsEmpty() { + senderLoginJID = pn + } + } return bridgev2.EventSender{ IsFromMe: id.User == wa.GetStore().GetJID().User || id.User == wa.GetStore().GetLID().User, Sender: waid.MakeUserID(id), - SenderLogin: waid.MakeUserLoginID(id), // TODO add support for lids here by looking up pn jid + SenderLogin: waid.MakeUserLoginID(senderLoginJID), } } diff --git a/pkg/connector/startchat.go b/pkg/connector/startchat.go index 7443ed3..039fef7 100644 --- a/pkg/connector/startchat.go +++ b/pkg/connector/startchat.go @@ -110,14 +110,14 @@ func (wa *WhatsAppClient) startChatLIDToPN(ctx context.Context, jid types.JID) ( return jid, nil } -func (wa *WhatsAppClient) makeCreateChatResponse(jid, origJID types.JID) *bridgev2.CreateChatResponse { +func (wa *WhatsAppClient) makeCreateChatResponse(ctx context.Context, jid, origJID types.JID) *bridgev2.CreateChatResponse { var redirID networkid.UserID if origJID != jid { redirID = waid.MakeUserID(jid) } return &bridgev2.CreateChatResponse{ PortalKey: wa.makeWAPortalKey(jid), - PortalInfo: wa.wrapDMInfo(jid), + PortalInfo: wa.wrapDMInfo(ctx, jid), DMRedirectedTo: redirID, } } @@ -128,7 +128,7 @@ func (wa *WhatsAppClient) CreateChatWithGhost(ctx context.Context, ghost *bridge if err != nil { return nil, err } - return wa.makeCreateChatResponse(jid, origJID), nil + return wa.makeCreateChatResponse(ctx, jid, origJID), nil } func (wa *WhatsAppClient) ResolveIdentifier(ctx context.Context, identifier string, startChat bool) (*bridgev2.ResolveIdentifierResponse, error) { @@ -148,7 +148,7 @@ func (wa *WhatsAppClient) ResolveIdentifier(ctx context.Context, identifier stri return &bridgev2.ResolveIdentifierResponse{ Ghost: ghost, UserID: waid.MakeUserID(jid), - Chat: wa.makeCreateChatResponse(jid, origJID), + Chat: wa.makeCreateChatResponse(ctx, jid, origJID), }, nil } From 4e5c4ccf0cb31841889df31689f6d4bd9aecbe22 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 9 Jul 2025 15:56:14 +0300 Subject: [PATCH 053/276] handlewhatsapp: fix call notices in groups --- go.mod | 2 +- go.sum | 4 ++-- pkg/connector/handlewhatsapp.go | 14 +++++++++----- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index e74c308..ecbef9a 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.8.8 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250701221811-9adf672adc90 + go.mau.fi/whatsmeow v0.0.0-20250709125544-2ec5b51770ba golang.org/x/image v0.28.0 golang.org/x/net v0.41.0 golang.org/x/sync v0.15.0 diff --git a/go.sum b/go.sum index 9c6cce2..93fc89f 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ go.mau.fi/util v0.8.8 h1:OnuEEc/sIJFhnq4kFggiImUpcmnmL/xpvQMRu5Fiy5c= go.mau.fi/util v0.8.8/go.mod h1:Y/kS3loxTEhy8Vill513EtPXr+CRDdae+Xj2BXXMy/c= 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-20250701221811-9adf672adc90 h1:K9knC6oQMLPutKwDVnqdgrDEjMJ8D5nREE9Li3dH71k= -go.mau.fi/whatsmeow v0.0.0-20250701221811-9adf672adc90/go.mod h1:bEyyFvXlwr/18B2pOkdX1vWAx1+y1NJX+sCXVyw01UA= +go.mau.fi/whatsmeow v0.0.0-20250709125544-2ec5b51770ba h1:9dfTlcISkZluN/vHc6WMFJ0qX8WU5EKATtZV8+ovp8E= +go.mau.fi/whatsmeow v0.0.0-20250709125544-2ec5b51770ba/go.mod h1:bEyyFvXlwr/18B2pOkdX1vWAx1+y1NJX+sCXVyw01UA= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index bd3f8e3..3db5419 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -86,9 +86,9 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) (success bool) { return wa.handleWAUndecryptableMessage(evt) case *events.CallOffer: - wa.handleWACallStart(ctx, evt.CallCreator, evt.CallID, "", evt.Timestamp) + wa.handleWACallStart(ctx, evt.GroupJID, evt.CallCreator, evt.CallID, "", evt.Timestamp) case *events.CallOfferNotice: - wa.handleWACallStart(ctx, evt.CallCreator, evt.CallID, evt.Type, evt.Timestamp) + wa.handleWACallStart(ctx, evt.GroupJID, evt.CallCreator, evt.CallID, evt.Type, evt.Timestamp) case *events.CallTerminate, *events.CallRelayLatency, *events.CallAccept, *events.UnknownCallEvent: // ignore case *events.IdentityChange: @@ -443,21 +443,25 @@ func (wa *WhatsAppClient) handleWALogout(reason events.ConnectFailureReason, onC const callEventMaxAge = 15 * time.Minute -func (wa *WhatsAppClient) handleWACallStart(ctx context.Context, sender types.JID, id, callType string, ts time.Time) { +func (wa *WhatsAppClient) handleWACallStart(ctx context.Context, group, sender types.JID, id, callType string, ts time.Time) { if !wa.Main.Config.CallStartNotices || time.Since(ts) > callEventMaxAge { return } + chat := group + if chat.IsEmpty() { + chat = sender + } wa.UserLogin.QueueRemoteEvent(&simplevent.Message[string]{ EventMeta: simplevent.EventMeta{ Type: bridgev2.RemoteEventMessage, LogContext: nil, - PortalKey: wa.makeWAPortalKey(sender), + PortalKey: wa.makeWAPortalKey(chat), Sender: wa.makeEventSender(ctx, sender), CreatePortal: true, Timestamp: ts, }, Data: callType, - ID: waid.MakeFakeMessageID(sender, sender, "call-"+id), + ID: waid.MakeFakeMessageID(chat, sender, "call-"+id), ConvertMessageFunc: convertCallStart, }) } From 6c023f0290c8ad896f79f8976d32da1d395a2654 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 9 Jul 2025 16:37:28 +0300 Subject: [PATCH 054/276] userinfo: fix log if next ghost resync isn't scheduled --- pkg/connector/userinfo.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/connector/userinfo.go b/pkg/connector/userinfo.go index 848359c..2f5d805 100644 --- a/pkg/connector/userinfo.go +++ b/pkg/connector/userinfo.go @@ -32,9 +32,13 @@ func (wa *WhatsAppClient) EnqueueGhostResync(ghost *bridgev2.Ghost) { jid := waid.ParseUserID(ghost.ID) if _, exists := wa.resyncQueue[jid]; !exists { wa.resyncQueue[jid] = resyncQueueItem{ghost: ghost} + nextResyncIn := time.Until(wa.nextResync).String() + if wa.nextResync.IsZero() { + nextResyncIn = "never" + } wa.UserLogin.Log.Debug(). Stringer("jid", jid). - Stringer("next_resync_in", time.Until(wa.nextResync)). + Str("next_resync_in", nextResyncIn). Msg("Enqueued resync for ghost") } wa.resyncQueueLock.Unlock() From 3fdd4e035f65ee44187e5085745307906d7a410f Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 10 Jul 2025 00:24:15 +0300 Subject: [PATCH 055/276] client: fix logger in whatsmeow background context --- 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 cfd6374..e1b01d3 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -79,7 +79,7 @@ func (wa *WhatsAppConnector) LoadUserLogin(ctx context.Context, login *bridgev2. w.Client.AutomaticMessageRerequestFromPhone = true w.Client.GetMessageForRetry = w.trackNotFoundRetry w.Client.PreRetryCallback = w.trackFoundRetry - w.Client.BackgroundEventCtx = wa.Bridge.BackgroundCtx + w.Client.BackgroundEventCtx = w.UserLogin.Log.WithContext(wa.Bridge.BackgroundCtx) w.Client.SetForceActiveDeliveryReceipts(wa.Config.ForceActiveDeliveryReceipts) w.Client.InitialAutoReconnect = wa.Config.InitialAutoReconnect } else { @@ -228,7 +228,7 @@ func (wa *WhatsAppClient) ConnectBackground(ctx context.Context, params *bridgev if wa.Client == nil { return bridgev2.ErrNotLoggedIn } - wa.Client.BackgroundEventCtx = wa.Main.Bridge.BackgroundCtx + wa.Client.BackgroundEventCtx = wa.UserLogin.Log.WithContext(wa.Main.Bridge.BackgroundCtx) wa.offlineSyncWaiter = make(chan error) wa.Main.backgroundConnectOnce.Do(wa.Main.onFirstBackgroundConnect) if err := wa.Main.updateProxy(ctx, wa.Client, false); err != nil { From 559a09bb4a71da56ea0b91a0f859a87ea5c28e09 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 10 Jul 2025 00:26:43 +0300 Subject: [PATCH 056/276] 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 ecbef9a..eb73f54 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.8.8 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250709125544-2ec5b51770ba + go.mau.fi/whatsmeow v0.0.0-20250709212552-0b8557ee0860 golang.org/x/image v0.28.0 golang.org/x/net v0.41.0 golang.org/x/sync v0.15.0 diff --git a/go.sum b/go.sum index 93fc89f..0170fc3 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ go.mau.fi/util v0.8.8 h1:OnuEEc/sIJFhnq4kFggiImUpcmnmL/xpvQMRu5Fiy5c= go.mau.fi/util v0.8.8/go.mod h1:Y/kS3loxTEhy8Vill513EtPXr+CRDdae+Xj2BXXMy/c= 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-20250709125544-2ec5b51770ba h1:9dfTlcISkZluN/vHc6WMFJ0qX8WU5EKATtZV8+ovp8E= -go.mau.fi/whatsmeow v0.0.0-20250709125544-2ec5b51770ba/go.mod h1:bEyyFvXlwr/18B2pOkdX1vWAx1+y1NJX+sCXVyw01UA= +go.mau.fi/whatsmeow v0.0.0-20250709212552-0b8557ee0860 h1:0AdAzM/QtzgUKiJ2oyVfiSiZq/fIjxadUtepPy2lZwk= +go.mau.fi/whatsmeow v0.0.0-20250709212552-0b8557ee0860/go.mod h1:bEyyFvXlwr/18B2pOkdX1vWAx1+y1NJX+sCXVyw01UA= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= From 3c4ee7c17a25ed5bdda399a8664014388f4907d0 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 10 Jul 2025 00:26:51 +0300 Subject: [PATCH 057/276] changelog: update --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 17d1e72..3044d45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# v0.12.3 (unreleased) + +* Further improved support for `@lid` users. +* Added automatic conversion when sending quicktime/mov videos to WhatsApp. +* Fixed disappearing message timer not automatically fixing itself in some cases. +* Fixed call notices being sent to DM portal even if the call was in a group. + # v0.12.2 (2025-06-16) * Improved support for `@lid` users. From d8812597733126b8958fb280f063768ed8283225 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 10 Jul 2025 17:57:31 +0300 Subject: [PATCH 058/276] handlewhatsapp: use simpler queue remote event call --- pkg/connector/backfill.go | 2 +- pkg/connector/handlewhatsapp.go | 14 +++++++------- pkg/connector/userinfo.go | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pkg/connector/backfill.go b/pkg/connector/backfill.go index c0359cf..8c29d22 100644 --- a/pkg/connector/backfill.go +++ b/pkg/connector/backfill.go @@ -338,7 +338,7 @@ func (wa *WhatsAppClient) createPortalsFromHistorySync(ctx context.Context) { wg.Done() continue } - wa.Main.Bridge.QueueRemoteEvent(wa.UserLogin, &simplevent.ChatResync{ + wa.UserLogin.QueueRemoteEvent(&simplevent.ChatResync{ EventMeta: simplevent.EventMeta{ Type: bridgev2.RemoteEventChatResync, LogContext: func(c zerolog.Context) zerolog.Context { diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index 3db5419..fa4e7e1 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -309,7 +309,7 @@ func (wa *WhatsAppClient) handleWAMessage(ctx context.Context, evt *events.Messa evt.Message = decrypted } } - res := wa.Main.Bridge.QueueRemoteEvent(wa.UserLogin, &WAMessageEvent{ + res := wa.UserLogin.QueueRemoteEvent(&WAMessageEvent{ MessageInfoWrapper: &MessageInfoWrapper{ Info: evt.Info, wa: wa, @@ -335,7 +335,7 @@ func (wa *WhatsAppClient) handleWAUndecryptableMessage(evt *events.Undecryptable if evt.Info.Chat == types.StatusBroadcastJID && !wa.Main.Config.EnableStatusBroadcast { return true } - res := wa.Main.Bridge.QueueRemoteEvent(wa.UserLogin, &WAUndecryptableMessage{ + res := wa.UserLogin.QueueRemoteEvent(&WAUndecryptableMessage{ MessageInfoWrapper: &MessageInfoWrapper{ Info: evt.Info, wa: wa, @@ -389,7 +389,7 @@ func (wa *WhatsAppClient) handleWAReceipt(ctx context.Context, evt *events.Recei for i, id := range evt.MessageIDs { targets[i] = waid.MakeMessageID(evt.Chat, messageSender, id) } - res := wa.Main.Bridge.QueueRemoteEvent(wa.UserLogin, &simplevent.Receipt{ + res := wa.UserLogin.QueueRemoteEvent(&simplevent.Receipt{ EventMeta: simplevent.EventMeta{ Type: evtType, PortalKey: wa.makeWAPortalKey(evt.Chat), @@ -411,7 +411,7 @@ func (wa *WhatsAppClient) handleWAChatPresence(ctx context.Context, evt *events. timeout = 0 } - wa.Main.Bridge.QueueRemoteEvent(wa.UserLogin, &simplevent.Typing{ + wa.UserLogin.QueueRemoteEvent(&simplevent.Typing{ EventMeta: simplevent.EventMeta{ Type: bridgev2.RemoteEventTyping, LogContext: nil, @@ -636,7 +636,7 @@ func (wa *WhatsAppClient) handleWAGroupInfoChange(ctx context.Context, evt *even } func (wa *WhatsAppClient) handleWAJoinedGroup(ctx context.Context, evt *events.JoinedGroup) { - wa.Main.Bridge.QueueRemoteEvent(wa.UserLogin, &simplevent.ChatResync{ + wa.UserLogin.QueueRemoteEvent(&simplevent.ChatResync{ EventMeta: simplevent.EventMeta{ Type: bridgev2.RemoteEventChatResync, LogContext: nil, @@ -648,7 +648,7 @@ func (wa *WhatsAppClient) handleWAJoinedGroup(ctx context.Context, evt *events.J } func (wa *WhatsAppClient) handleWANewsletterJoin(ctx context.Context, evt *events.NewsletterJoin) { - wa.Main.Bridge.QueueRemoteEvent(wa.UserLogin, &simplevent.ChatResync{ + wa.UserLogin.QueueRemoteEvent(&simplevent.ChatResync{ EventMeta: simplevent.EventMeta{ Type: bridgev2.RemoteEventChatResync, LogContext: nil, @@ -660,7 +660,7 @@ func (wa *WhatsAppClient) handleWANewsletterJoin(ctx context.Context, evt *event } func (wa *WhatsAppClient) handleWANewsletterLeave(evt *events.NewsletterLeave) { - wa.Main.Bridge.QueueRemoteEvent(wa.UserLogin, &simplevent.ChatDelete{ + wa.UserLogin.QueueRemoteEvent(&simplevent.ChatDelete{ EventMeta: simplevent.EventMeta{ Type: bridgev2.RemoteEventChatDelete, LogContext: nil, diff --git a/pkg/connector/userinfo.go b/pkg/connector/userinfo.go index 2f5d805..4d7d982 100644 --- a/pkg/connector/userinfo.go +++ b/pkg/connector/userinfo.go @@ -128,7 +128,7 @@ func (wa *WhatsAppClient) doGhostResync(ctx context.Context, queue map[types.JID } } for _, portal := range portals { - wa.Main.Bridge.QueueRemoteEvent(wa.UserLogin, &simplevent.ChatResync{ + wa.UserLogin.QueueRemoteEvent(&simplevent.ChatResync{ EventMeta: simplevent.EventMeta{ Type: bridgev2.RemoteEventChatResync, LogContext: func(c zerolog.Context) zerolog.Context { From aa3bb1ea5ea98b61039c722a60fdaea289100e15 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 10 Jul 2025 18:03:16 +0300 Subject: [PATCH 059/276] handlewhatsapp,backfill: propagate QueueRemoteEvent results in more places --- pkg/connector/backfill.go | 6 +- pkg/connector/handlewhatsapp.go | 116 ++++++++++++++++---------------- 2 files changed, 64 insertions(+), 58 deletions(-) diff --git a/pkg/connector/backfill.go b/pkg/connector/backfill.go index 8c29d22..81346b9 100644 --- a/pkg/connector/backfill.go +++ b/pkg/connector/backfill.go @@ -338,7 +338,7 @@ func (wa *WhatsAppClient) createPortalsFromHistorySync(ctx context.Context) { wg.Done() continue } - wa.UserLogin.QueueRemoteEvent(&simplevent.ChatResync{ + res := wa.UserLogin.QueueRemoteEvent(&simplevent.ChatResync{ EventMeta: simplevent.EventMeta{ Type: bridgev2.RemoteEventChatResync, LogContext: func(c zerolog.Context) zerolog.Context { @@ -359,6 +359,10 @@ func (wa *WhatsAppClient) createPortalsFromHistorySync(ctx context.Context) { ChatInfo: wrappedInfo, LatestMessageTS: conv.LastMessageTimestamp, }) + if !res.Success { + log.Debug().Msg("Cancelling history sync portal creation loop") + return + } } log.Info().Int("conversation_count", len(conversations)).Msg("Finished creating portals from history sync") go func() { diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index fa4e7e1..37943bd 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -75,36 +75,37 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) (success bool) { log := wa.UserLogin.Log ctx := log.WithContext(wa.Main.Bridge.BackgroundCtx) + success = true switch evt := rawEvt.(type) { case *events.Message: - return wa.handleWAMessage(ctx, evt) + success = wa.handleWAMessage(ctx, evt) case *events.Receipt: - return wa.handleWAReceipt(ctx, evt) + success = wa.handleWAReceipt(ctx, evt) case *events.ChatPresence: wa.handleWAChatPresence(ctx, evt) case *events.UndecryptableMessage: - return wa.handleWAUndecryptableMessage(evt) + success = wa.handleWAUndecryptableMessage(evt) case *events.CallOffer: - wa.handleWACallStart(ctx, evt.GroupJID, evt.CallCreator, evt.CallID, "", evt.Timestamp) + success = wa.handleWACallStart(ctx, evt.GroupJID, evt.CallCreator, evt.CallID, "", evt.Timestamp) case *events.CallOfferNotice: - wa.handleWACallStart(ctx, evt.GroupJID, evt.CallCreator, evt.CallID, evt.Type, evt.Timestamp) + success = wa.handleWACallStart(ctx, evt.GroupJID, evt.CallCreator, evt.CallID, evt.Type, evt.Timestamp) case *events.CallTerminate, *events.CallRelayLatency, *events.CallAccept, *events.UnknownCallEvent: // ignore case *events.IdentityChange: wa.handleWAIdentityChange(ctx, evt) case *events.MarkChatAsRead: - wa.handleWAMarkChatAsRead(ctx, evt) + success = wa.handleWAMarkChatAsRead(ctx, evt) case *events.DeleteForMe: - wa.handleWADeleteForMe(evt) + success = wa.handleWADeleteForMe(evt) case *events.DeleteChat: - wa.handleWADeleteChat(evt) + success = wa.handleWADeleteChat(evt) case *events.Mute: - wa.handleWAMute(evt) + success = wa.handleWAMute(evt) case *events.Archive: - wa.handleWAArchive(evt) + success = wa.handleWAArchive(evt) case *events.Pin: - wa.handleWAPin(evt) + success = wa.handleWAPin(evt) case *events.HistorySync: if wa.Main.Bridge.Config.Backfill.Enabled { @@ -112,18 +113,18 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) (success bool) { } case *events.MediaRetry: wa.phoneSeen(evt.Timestamp) - wa.UserLogin.QueueRemoteEvent(&WAMediaRetry{MediaRetry: evt, wa: wa}) + success = wa.UserLogin.QueueRemoteEvent(&WAMediaRetry{MediaRetry: evt, wa: wa}).Success case *events.GroupInfo: - wa.handleWAGroupInfoChange(ctx, evt) + success = wa.handleWAGroupInfoChange(ctx, evt) case *events.JoinedGroup: - wa.handleWAJoinedGroup(ctx, evt) + success = wa.handleWAJoinedGroup(ctx, evt) case *events.NewsletterJoin: - wa.handleWANewsletterJoin(ctx, evt) + success = wa.handleWANewsletterJoin(ctx, evt) case *events.NewsletterLeave: - wa.handleWANewsletterLeave(evt) + success = wa.handleWANewsletterLeave(evt) case *events.Picture: - go wa.handleWAPictureUpdate(ctx, evt) + success = wa.handleWAPictureUpdate(ctx, evt) case *events.AppStateSyncComplete: if len(wa.GetStore().PushName) > 0 && evt.Name == appstate.WAPatchCriticalBlock { @@ -253,7 +254,7 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) (success bool) { default: log.Debug().Type("event_type", rawEvt).Msg("Unhandled WhatsApp event") } - return true + return } func (wa *WhatsAppClient) handleWAMessage(ctx context.Context, evt *events.Message) (success bool) { @@ -443,15 +444,15 @@ func (wa *WhatsAppClient) handleWALogout(reason events.ConnectFailureReason, onC const callEventMaxAge = 15 * time.Minute -func (wa *WhatsAppClient) handleWACallStart(ctx context.Context, group, sender types.JID, id, callType string, ts time.Time) { +func (wa *WhatsAppClient) handleWACallStart(ctx context.Context, group, sender types.JID, id, callType string, ts time.Time) bool { if !wa.Main.Config.CallStartNotices || time.Since(ts) > callEventMaxAge { - return + return true } chat := group if chat.IsEmpty() { chat = sender } - wa.UserLogin.QueueRemoteEvent(&simplevent.Message[string]{ + return wa.UserLogin.QueueRemoteEvent(&simplevent.Message[string]{ EventMeta: simplevent.EventMeta{ Type: bridgev2.RemoteEventMessage, LogContext: nil, @@ -463,7 +464,7 @@ func (wa *WhatsAppClient) handleWACallStart(ctx context.Context, group, sender t Data: callType, ID: waid.MakeFakeMessageID(chat, sender, "call-"+id), ConvertMessageFunc: convertCallStart, - }) + }).Success } func convertCallStart(ctx context.Context, portal *bridgev2.Portal, intent bridgev2.MatrixAPI, callType string) (*bridgev2.ConvertedMessage, error) { @@ -521,19 +522,19 @@ func convertIdentityChange(ctx context.Context, portal *bridgev2.Portal, intent }, nil } -func (wa *WhatsAppClient) handleWADeleteChat(evt *events.DeleteChat) { - wa.UserLogin.QueueRemoteEvent(&simplevent.ChatDelete{ +func (wa *WhatsAppClient) handleWADeleteChat(evt *events.DeleteChat) bool { + return wa.UserLogin.QueueRemoteEvent(&simplevent.ChatDelete{ EventMeta: simplevent.EventMeta{ Type: bridgev2.RemoteEventChatDelete, PortalKey: wa.makeWAPortalKey(evt.JID), Timestamp: evt.Timestamp, }, OnlyForMe: true, - }) + }).Success } -func (wa *WhatsAppClient) handleWADeleteForMe(evt *events.DeleteForMe) { - wa.UserLogin.QueueRemoteEvent(&simplevent.MessageRemove{ +func (wa *WhatsAppClient) handleWADeleteForMe(evt *events.DeleteForMe) bool { + return wa.UserLogin.QueueRemoteEvent(&simplevent.MessageRemove{ EventMeta: simplevent.EventMeta{ Type: bridgev2.RemoteEventMessageRemove, PortalKey: wa.makeWAPortalKey(evt.ChatJID), @@ -541,11 +542,11 @@ func (wa *WhatsAppClient) handleWADeleteForMe(evt *events.DeleteForMe) { }, TargetMessage: waid.MakeMessageID(evt.ChatJID, evt.SenderJID, evt.MessageID), OnlyForMe: true, - }) + }).Success } -func (wa *WhatsAppClient) handleWAMarkChatAsRead(ctx context.Context, evt *events.MarkChatAsRead) { - wa.UserLogin.QueueRemoteEvent(&simplevent.Receipt{ +func (wa *WhatsAppClient) handleWAMarkChatAsRead(ctx context.Context, evt *events.MarkChatAsRead) bool { + return wa.UserLogin.QueueRemoteEvent(&simplevent.Receipt{ EventMeta: simplevent.EventMeta{ Type: bridgev2.RemoteEventReadReceipt, PortalKey: wa.makeWAPortalKey(evt.JID), @@ -553,7 +554,7 @@ func (wa *WhatsAppClient) handleWAMarkChatAsRead(ctx context.Context, evt *event Timestamp: evt.Timestamp, }, ReadUpTo: evt.Timestamp, - }) + }).Success } func (wa *WhatsAppClient) syncGhost(jid types.JID, reason string, pictureID *string) { @@ -582,9 +583,10 @@ func (wa *WhatsAppClient) syncGhost(jid types.JID, reason string, pictureID *str go wa.syncRemoteProfile(ctx, ghost) } -func (wa *WhatsAppClient) handleWAPictureUpdate(ctx context.Context, evt *events.Picture) { +func (wa *WhatsAppClient) handleWAPictureUpdate(ctx context.Context, evt *events.Picture) bool { if evt.JID.Server == types.DefaultUserServer || evt.JID.Server == types.BotServer { - wa.syncGhost(evt.JID, "picture event", &evt.PictureID) + go wa.syncGhost(evt.JID, "picture event", &evt.PictureID) + return true } else { var changes bridgev2.ChatInfo if evt.Remove { @@ -592,7 +594,7 @@ func (wa *WhatsAppClient) handleWAPictureUpdate(ctx context.Context, evt *events } else { changes.ExtraUpdates = wa.makePortalAvatarFetcher(evt.PictureID, evt.Author, evt.Timestamp) } - wa.UserLogin.QueueRemoteEvent(&simplevent.ChatInfoChange{ + return wa.UserLogin.QueueRemoteEvent(&simplevent.ChatInfoChange{ EventMeta: simplevent.EventMeta{ Type: bridgev2.RemoteEventChatInfoChange, LogContext: func(c zerolog.Context) zerolog.Context { @@ -609,11 +611,11 @@ func (wa *WhatsAppClient) handleWAPictureUpdate(ctx context.Context, evt *events ChatInfoChange: &bridgev2.ChatInfoChange{ ChatInfo: &changes, }, - }) + }).Success } } -func (wa *WhatsAppClient) handleWAGroupInfoChange(ctx context.Context, evt *events.GroupInfo) { +func (wa *WhatsAppClient) handleWAGroupInfoChange(ctx context.Context, evt *events.GroupInfo) bool { eventMeta := simplevent.EventMeta{ Type: bridgev2.RemoteEventChatInfoChange, LogContext: nil, @@ -626,17 +628,17 @@ func (wa *WhatsAppClient) handleWAGroupInfoChange(ctx context.Context, evt *even } if evt.Delete != nil { eventMeta.Type = bridgev2.RemoteEventChatDelete - wa.UserLogin.QueueRemoteEvent(&simplevent.ChatDelete{EventMeta: eventMeta}) + return wa.UserLogin.QueueRemoteEvent(&simplevent.ChatDelete{EventMeta: eventMeta}).Success } else { - wa.UserLogin.QueueRemoteEvent(&simplevent.ChatInfoChange{ + return wa.UserLogin.QueueRemoteEvent(&simplevent.ChatInfoChange{ EventMeta: eventMeta, ChatInfoChange: wa.wrapGroupInfoChange(ctx, evt), - }) + }).Success } } -func (wa *WhatsAppClient) handleWAJoinedGroup(ctx context.Context, evt *events.JoinedGroup) { - wa.UserLogin.QueueRemoteEvent(&simplevent.ChatResync{ +func (wa *WhatsAppClient) handleWAJoinedGroup(ctx context.Context, evt *events.JoinedGroup) bool { + return wa.UserLogin.QueueRemoteEvent(&simplevent.ChatResync{ EventMeta: simplevent.EventMeta{ Type: bridgev2.RemoteEventChatResync, LogContext: nil, @@ -644,11 +646,11 @@ func (wa *WhatsAppClient) handleWAJoinedGroup(ctx context.Context, evt *events.J CreatePortal: true, }, ChatInfo: wa.wrapGroupInfo(ctx, &evt.GroupInfo), - }) + }).Success } -func (wa *WhatsAppClient) handleWANewsletterJoin(ctx context.Context, evt *events.NewsletterJoin) { - wa.UserLogin.QueueRemoteEvent(&simplevent.ChatResync{ +func (wa *WhatsAppClient) handleWANewsletterJoin(ctx context.Context, evt *events.NewsletterJoin) bool { + return wa.UserLogin.QueueRemoteEvent(&simplevent.ChatResync{ EventMeta: simplevent.EventMeta{ Type: bridgev2.RemoteEventChatResync, LogContext: nil, @@ -656,22 +658,22 @@ func (wa *WhatsAppClient) handleWANewsletterJoin(ctx context.Context, evt *event CreatePortal: true, }, ChatInfo: wa.wrapNewsletterInfo(ctx, &evt.NewsletterMetadata), - }) + }).Success } -func (wa *WhatsAppClient) handleWANewsletterLeave(evt *events.NewsletterLeave) { - wa.UserLogin.QueueRemoteEvent(&simplevent.ChatDelete{ +func (wa *WhatsAppClient) handleWANewsletterLeave(evt *events.NewsletterLeave) bool { + return wa.UserLogin.QueueRemoteEvent(&simplevent.ChatDelete{ EventMeta: simplevent.EventMeta{ Type: bridgev2.RemoteEventChatDelete, LogContext: nil, PortalKey: wa.makeWAPortalKey(evt.ID), }, OnlyForMe: true, - }) + }).Success } -func (wa *WhatsAppClient) handleWAUserLocalPortalInfo(chatJID types.JID, ts time.Time, info *bridgev2.UserLocalPortalInfo) { - wa.UserLogin.QueueRemoteEvent(&simplevent.ChatInfoChange{ +func (wa *WhatsAppClient) handleWAUserLocalPortalInfo(chatJID types.JID, ts time.Time, info *bridgev2.UserLocalPortalInfo) bool { + return wa.UserLogin.QueueRemoteEvent(&simplevent.ChatInfoChange{ EventMeta: simplevent.EventMeta{ Type: bridgev2.RemoteEventChatInfoChange, PortalKey: wa.makeWAPortalKey(chatJID), @@ -682,10 +684,10 @@ func (wa *WhatsAppClient) handleWAUserLocalPortalInfo(chatJID types.JID, ts time UserLocal: info, }, }, - }) + }).Success } -func (wa *WhatsAppClient) handleWAMute(evt *events.Mute) { +func (wa *WhatsAppClient) handleWAMute(evt *events.Mute) bool { var mutedUntil time.Time if evt.Action.GetMuted() { mutedUntil = event.MutedForever @@ -695,27 +697,27 @@ func (wa *WhatsAppClient) handleWAMute(evt *events.Mute) { } else { mutedUntil = bridgev2.Unmuted } - wa.handleWAUserLocalPortalInfo(evt.JID, evt.Timestamp, &bridgev2.UserLocalPortalInfo{ + return wa.handleWAUserLocalPortalInfo(evt.JID, evt.Timestamp, &bridgev2.UserLocalPortalInfo{ MutedUntil: &mutedUntil, }) } -func (wa *WhatsAppClient) handleWAArchive(evt *events.Archive) { +func (wa *WhatsAppClient) handleWAArchive(evt *events.Archive) bool { var tag event.RoomTag if evt.Action.GetArchived() { tag = wa.Main.Config.ArchiveTag } - wa.handleWAUserLocalPortalInfo(evt.JID, evt.Timestamp, &bridgev2.UserLocalPortalInfo{ + return wa.handleWAUserLocalPortalInfo(evt.JID, evt.Timestamp, &bridgev2.UserLocalPortalInfo{ Tag: &tag, }) } -func (wa *WhatsAppClient) handleWAPin(evt *events.Pin) { +func (wa *WhatsAppClient) handleWAPin(evt *events.Pin) bool { var tag event.RoomTag if evt.Action.GetPinned() { tag = wa.Main.Config.PinnedTag } - wa.handleWAUserLocalPortalInfo(evt.JID, evt.Timestamp, &bridgev2.UserLocalPortalInfo{ + return wa.handleWAUserLocalPortalInfo(evt.JID, evt.Timestamp, &bridgev2.UserLocalPortalInfo{ Tag: &tag, }) } From 3dd9b894d0519a4471b89674de6e9d991907f704 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 16 Jul 2025 11:44:44 +0300 Subject: [PATCH 060/276] Bump version to v0.12.3 --- CHANGELOG.md | 2 +- cmd/mautrix-whatsapp/main.go | 2 +- go.mod | 20 ++++++++++---------- go.sum | 36 ++++++++++++++++++------------------ 4 files changed, 30 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3044d45..c9a9983 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# v0.12.3 (unreleased) +# v0.12.3 (2025-07-16) * Further improved support for `@lid` users. * Added automatic conversion when sending quicktime/mov videos to WhatsApp. diff --git a/cmd/mautrix-whatsapp/main.go b/cmd/mautrix-whatsapp/main.go index 40170f5..0ec230f 100644 --- a/cmd/mautrix-whatsapp/main.go +++ b/cmd/mautrix-whatsapp/main.go @@ -23,7 +23,7 @@ var m = mxmain.BridgeMain{ Name: "mautrix-whatsapp", URL: "https://github.com/mautrix/whatsapp", Description: "A Matrix-WhatsApp puppeting bridge.", - Version: "0.12.2", + Version: "0.12.3", Connector: c, } diff --git a/go.mod b/go.mod index eb73f54..402d48c 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module go.mau.fi/mautrix-whatsapp go 1.23.0 -toolchain go1.24.4 +toolchain go1.24.5 require ( github.com/gorilla/mux v1.8.0 @@ -11,13 +11,13 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.8.8 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250709212552-0b8557ee0860 - golang.org/x/image v0.28.0 - golang.org/x/net v0.41.0 - golang.org/x/sync v0.15.0 + go.mau.fi/whatsmeow v0.0.0-20250716083826-71ea87a455e7 + golang.org/x/image v0.29.0 + golang.org/x/net v0.42.0 + golang.org/x/sync v0.16.0 google.golang.org/protobuf v1.36.6 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.24.2-0.20250703182219-b62535edaa57 + maunium.net/go/mautrix v0.24.2 ) require ( @@ -39,10 +39,10 @@ require ( github.com/yuin/goldmark v1.7.12 // indirect go.mau.fi/libsignal v0.2.0 // indirect go.mau.fi/zeroconfig v0.1.3 // indirect - golang.org/x/crypto v0.39.0 // indirect - golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476 // indirect - golang.org/x/sys v0.33.0 // indirect - golang.org/x/text v0.26.0 // indirect + golang.org/x/crypto v0.40.0 // indirect + golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc // indirect + golang.org/x/sys v0.34.0 // indirect + golang.org/x/text v0.27.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 0170fc3..67342eb 100644 --- a/go.sum +++ b/go.sum @@ -69,27 +69,27 @@ go.mau.fi/util v0.8.8 h1:OnuEEc/sIJFhnq4kFggiImUpcmnmL/xpvQMRu5Fiy5c= go.mau.fi/util v0.8.8/go.mod h1:Y/kS3loxTEhy8Vill513EtPXr+CRDdae+Xj2BXXMy/c= 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-20250709212552-0b8557ee0860 h1:0AdAzM/QtzgUKiJ2oyVfiSiZq/fIjxadUtepPy2lZwk= -go.mau.fi/whatsmeow v0.0.0-20250709212552-0b8557ee0860/go.mod h1:bEyyFvXlwr/18B2pOkdX1vWAx1+y1NJX+sCXVyw01UA= +go.mau.fi/whatsmeow v0.0.0-20250716083826-71ea87a455e7 h1:qxn0LG8cfPMzzy5Aq2eri+9m2qbpxvMzQdULHgAE3Bk= +go.mau.fi/whatsmeow v0.0.0-20250716083826-71ea87a455e7/go.mod h1:ltDTXUgOAT7LcFKp11H+5S7UY7+xHBMGzNJcv3dLHGk= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= -golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= -golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= -golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476 h1:bsqhLWFR6G6xiQcb+JoGqdKdRU6WzPWmK8E0jxTjzo4= -golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= -golang.org/x/image v0.28.0 h1:gdem5JW1OLS4FbkWgLO+7ZeFzYtL3xClb97GaUzYMFE= -golang.org/x/image v0.28.0/go.mod h1:GUJYXtnGKEUgggyzh+Vxt+AviiCcyiwpsl8iQ8MvwGY= -golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= -golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= -golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= -golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= +golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= +golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc h1:TS73t7x3KarrNd5qAipmspBDS1rkMcgVG/fS1aRb4Rc= +golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc= +golang.org/x/image v0.29.0 h1:HcdsyR4Gsuys/Axh0rDEmlBmB68rW1U9BUdB3UVHsas= +golang.org/x/image v0.29.0/go.mod h1:RVJROnf3SLK8d26OW91j4FrIHGbsJ8QnbEocVTOWQDA= +golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= +golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 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.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= -golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= +golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= +golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= +golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -101,5 +101,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.24.2-0.20250703182219-b62535edaa57 h1:z7/37EKswVuTiW26dF6lkPgf60JtKvxsOb0CfWrMeBI= -maunium.net/go/mautrix v0.24.2-0.20250703182219-b62535edaa57/go.mod h1:Xy6o+pXmbqmgWsUWh15EQ1eozjC+k/VT/7kloByv9PI= +maunium.net/go/mautrix v0.24.2 h1:+AVT5kbcA/QuT5svrJKp4ivwoUmz+RRplMp3DnfpheI= +maunium.net/go/mautrix v0.24.2/go.mod h1:1ut900w++eE9by9yqCR2dQdMqwsHwZG5L+1bKB1EvSA= From 1183251621f272c50073518668bd0964eace8ce5 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 17 Jul 2025 11:22:53 +0300 Subject: [PATCH 061/276] .github: update issue templates [skip ci] --- .github/ISSUE_TEMPLATE/bug.md | 10 ++++++---- .github/ISSUE_TEMPLATE/enhancement.md | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug.md b/.github/ISSUE_TEMPLATE/bug.md index 18862a5..3703df9 100644 --- a/.github/ISSUE_TEMPLATE/bug.md +++ b/.github/ISSUE_TEMPLATE/bug.md @@ -1,14 +1,16 @@ --- name: Bug report about: If something is definitely wrong in the bridge (rather than just a setup issue), - file a bug report. Remember to include relevant logs. -labels: bug + file a bug report. Remember to include relevant logs. Asking in the Matrix room first + is strongly recommended. +type: Bug --- diff --git a/.github/ISSUE_TEMPLATE/enhancement.md b/.github/ISSUE_TEMPLATE/enhancement.md index 264e67f..a04fe58 100644 --- a/.github/ISSUE_TEMPLATE/enhancement.md +++ b/.github/ISSUE_TEMPLATE/enhancement.md @@ -1,6 +1,6 @@ --- name: Enhancement request about: Submit a feature request or other suggestion -labels: enhancement +type: Feature --- From 0be53e58c63ccd90d93631f12115176ce4be9abf Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 17 Jul 2025 11:42:11 +0300 Subject: [PATCH 062/276] waid/dbmeta: remove unnecessary LID field in user login metadata --- go.mod | 2 +- go.sum | 4 ++-- pkg/connector/handlewhatsapp.go | 14 -------------- pkg/connector/login.go | 1 - pkg/waid/dbmeta.go | 1 - 5 files changed, 3 insertions(+), 19 deletions(-) diff --git a/go.mod b/go.mod index 402d48c..800c749 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.8.8 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250716083826-71ea87a455e7 + go.mau.fi/whatsmeow v0.0.0-20250717084138-aecc878ab213 golang.org/x/image v0.29.0 golang.org/x/net v0.42.0 golang.org/x/sync v0.16.0 diff --git a/go.sum b/go.sum index 67342eb..5b9e23f 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ go.mau.fi/util v0.8.8 h1:OnuEEc/sIJFhnq4kFggiImUpcmnmL/xpvQMRu5Fiy5c= go.mau.fi/util v0.8.8/go.mod h1:Y/kS3loxTEhy8Vill513EtPXr+CRDdae+Xj2BXXMy/c= 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-20250716083826-71ea87a455e7 h1:qxn0LG8cfPMzzy5Aq2eri+9m2qbpxvMzQdULHgAE3Bk= -go.mau.fi/whatsmeow v0.0.0-20250716083826-71ea87a455e7/go.mod h1:ltDTXUgOAT7LcFKp11H+5S7UY7+xHBMGzNJcv3dLHGk= +go.mau.fi/whatsmeow v0.0.0-20250717084138-aecc878ab213 h1:CYkW6OUQk0uvzMa3wN7RtGd7H8DxYdYqw/DoFobTK2w= +go.mau.fi/whatsmeow v0.0.0-20250717084138-aecc878ab213/go.mod h1:ltDTXUgOAT7LcFKp11H+5S7UY7+xHBMGzNJcv3dLHGk= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index 37943bd..efd261d 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -169,20 +169,6 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) (success bool) { }() go wa.syncRemoteProfile(log.WithContext(context.Background()), nil) } - meta := wa.UserLogin.Metadata.(*waid.UserLoginMetadata) - if meta.WALID == "" { - meta.WALID = wa.GetStore().GetLID().User - if meta.WALID != "" { - go func() { - err := wa.UserLogin.Save(log.WithContext(context.Background())) - if err != nil { - log.Err(err).Msg("Failed to save user login metadata after updating LID") - } else { - log.Info().Msg("Updated LID in user login metadata") - } - }() - } - } case *events.OfflineSyncPreview: log.Info(). Int("message_count", evt.Messages). diff --git a/pkg/connector/login.go b/pkg/connector/login.go index d0c1718..5e8784e 100644 --- a/pkg/connector/login.go +++ b/pkg/connector/login.go @@ -321,7 +321,6 @@ func (wl *WALogin) Wait(ctx context.Context) (*bridgev2.LoginStep, error) { Name: wl.LoginSuccess.BusinessName, }, Metadata: &waid.UserLoginMetadata{ - WALID: wl.LoginSuccess.LID.User, WADeviceID: wl.LoginSuccess.ID.Device, Timezone: wl.Timezone, diff --git a/pkg/waid/dbmeta.go b/pkg/waid/dbmeta.go index 4e0cd66..c67f587 100644 --- a/pkg/waid/dbmeta.go +++ b/pkg/waid/dbmeta.go @@ -29,7 +29,6 @@ import ( type UserLoginMetadata struct { WADeviceID uint16 `json:"wa_device_id"` - WALID string `json:"wa_lid"` PhoneLastSeen jsontime.Unix `json:"phone_last_seen"` PhoneLastPinged jsontime.Unix `json:"phone_last_pinged"` Timezone string `json:"timezone"` From 825dc7728f6bd8d6d878cf2ea4558cc5450ded10 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 17 Jul 2025 15:13:21 +0300 Subject: [PATCH 063/276] events: force first message after resync to use lid ghost --- pkg/connector/events.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pkg/connector/events.go b/pkg/connector/events.go index 035b2d5..76eef7a 100644 --- a/pkg/connector/events.go +++ b/pkg/connector/events.go @@ -151,6 +151,13 @@ func (evt *WAMessageEvent) PreHandle(ctx context.Context, portal *bridgev2.Porta log.Info().Msg("Resyncing group members as it appears to have switched to LID addressing mode") portal.UpdateInfo(ctx, evt.wa.wrapGroupInfo(ctx, info), evt.wa.UserLogin, nil, time.Time{}) log.Debug().Msg("Finished resyncing after LID change") + if evt.Info.Sender.Server == types.DefaultUserServer && evt.Info.SenderAlt.Server == types.HiddenUserServer { + evt.Info.Sender, evt.Info.SenderAlt = evt.Info.SenderAlt, evt.Info.Sender + log.Debug(). + Stringer("new_sender", evt.Info.Sender). + Stringer("new_sender_alt", evt.Info.SenderAlt). + Msg("Overriding sender to LID after resyncing group members") + } } func (evt *WAMessageEvent) PostHandle(ctx context.Context, portal *bridgev2.Portal) { From e8697341dde12a76e64c7f044857c55776f7c860 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 17 Jul 2025 15:29:41 +0300 Subject: [PATCH 064/276] msgconv/from-whatsapp: add better placeholder for album messages --- pkg/connector/wamsgtype.go | 4 ---- pkg/msgconv/from-whatsapp.go | 2 ++ pkg/msgconv/wa-media.go | 22 ++++++++++++++++++++++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/pkg/connector/wamsgtype.go b/pkg/connector/wamsgtype.go index 7a8547c..92e7f86 100644 --- a/pkg/connector/wamsgtype.go +++ b/pkg/connector/wamsgtype.go @@ -124,10 +124,6 @@ func getMessageType(waMsg *waE2E.Message) string { case waMsg.EncEventResponseMessage != nil: return "ignore" // these are ignored for now as they're not meant to be shown as new messages //return "encrypted event response" - case waMsg.CommentMessage != nil: - return "comment" - case waMsg.EncCommentMessage != nil: - return "encrypted comment" case waMsg.NewsletterAdminInviteMessage != nil: return "newsletter admin invite" case waMsg.SecretEncryptedMessage != nil: diff --git a/pkg/msgconv/from-whatsapp.go b/pkg/msgconv/from-whatsapp.go index 11ef09a..1784ede 100644 --- a/pkg/msgconv/from-whatsapp.go +++ b/pkg/msgconv/from-whatsapp.go @@ -184,6 +184,8 @@ func (mc *MessageConverter) ToMatrix( part, contextInfo = mc.convertMediaMessage(ctx, waMsg.AudioMessage, typeName, info, isViewOnce, previouslyConvertedPart) case waMsg.DocumentMessage != nil: part, contextInfo = mc.convertMediaMessage(ctx, waMsg.DocumentMessage, "file attachment", info, isViewOnce, previouslyConvertedPart) + case waMsg.AlbumMessage != nil: + part, contextInfo = mc.convertAlbumMessage(ctx, waMsg.AlbumMessage) case waMsg.LocationMessage != nil: part, contextInfo = mc.convertLocationMessage(ctx, waMsg.LocationMessage) case waMsg.LiveLocationMessage != nil: diff --git a/pkg/msgconv/wa-media.go b/pkg/msgconv/wa-media.go index dc8b227..abff34e 100644 --- a/pkg/msgconv/wa-media.go +++ b/pkg/msgconv/wa-media.go @@ -119,6 +119,28 @@ func (mc *MessageConverter) convertMediaMessage( return } +func (mc *MessageConverter) convertAlbumMessage(ctx context.Context, msg *waE2E.AlbumMessage) (*bridgev2.ConvertedMessagePart, *waE2E.ContextInfo) { + parts := make([]string, 0, 2) + if msg.GetExpectedImageCount() > 0 { + parts = append(parts, fmt.Sprintf("%d images", msg.GetExpectedImageCount())) + } + if msg.GetExpectedVideoCount() > 0 { + parts = append(parts, fmt.Sprintf("%d videos", msg.GetExpectedVideoCount())) + } + var partDesc string + if len(parts) > 0 { + partDesc = fmt.Sprintf(" with %s", strings.Join(parts, " and ")) + } + body := fmt.Sprintf("Sent an album%s:", partDesc) + return &bridgev2.ConvertedMessagePart{ + Type: event.EventMessage, + Content: &event.MessageEventContent{ + MsgType: event.MsgNotice, + Body: body, + }, + }, msg.GetContextInfo() +} + const FailedMediaField = "fi.mau.whatsapp.failed_media" type FailedMediaKeys struct { From d61c197be50dda58e6b88bdae2913ba79d1a934e Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 22 Jul 2025 14:06:37 +0300 Subject: [PATCH 065/276] msgconv/from-matrix: try to escape formatting characters in plain text --- pkg/msgconv/from-matrix.go | 9 ++++++++- pkg/msgconv/msgconv.go | 3 +++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/pkg/msgconv/from-matrix.go b/pkg/msgconv/from-matrix.go index 105c1c2..edf33f1 100644 --- a/pkg/msgconv/from-matrix.go +++ b/pkg/msgconv/from-matrix.go @@ -279,6 +279,13 @@ func (mc *MessageConverter) constructMediaMessage( } } +var hackyFormattingEscaper = strings.NewReplacer( + "~", "\u200b~", + "_", "\u200b_", + "*", "\u200b*", + "`", "\u200b`", +) + func (mc *MessageConverter) parseText(ctx context.Context, content *event.MessageEventContent) (text string, mentions []string) { mentions = make([]string, 0) @@ -288,7 +295,7 @@ func (mc *MessageConverter) parseText(ctx context.Context, content *event.Messag if content.Format == event.FormatHTML { text = mc.HTMLParser.Parse(content.FormattedBody, parseCtx) } else { - text = content.Body + text = hackyFormattingEscaper.Replace(content.Body) } return } diff --git a/pkg/msgconv/msgconv.go b/pkg/msgconv/msgconv.go index e4109c8..daa00b5 100644 --- a/pkg/msgconv/msgconv.go +++ b/pkg/msgconv/msgconv.go @@ -69,6 +69,9 @@ func New(br *bridgev2.Bridge) *MessageConverter { MonospaceBlockConverter: func(code, language string, ctx format.Context) string { return "```\n" + code + "\n```" }, + TextConverter: func(s string, context format.Context) string { + return hackyFormattingEscaper.Replace(s) + }, } return mc } From cadc10719e8363159283e93e17a395f541548643 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 22 Jul 2025 14:07:54 +0300 Subject: [PATCH 066/276] 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 800c749..53354e5 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.8.8 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250717084138-aecc878ab213 + go.mau.fi/whatsmeow v0.0.0-20250722103218-f0d022d58b17 golang.org/x/image v0.29.0 golang.org/x/net v0.42.0 golang.org/x/sync v0.16.0 diff --git a/go.sum b/go.sum index 5b9e23f..a057254 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ go.mau.fi/util v0.8.8 h1:OnuEEc/sIJFhnq4kFggiImUpcmnmL/xpvQMRu5Fiy5c= go.mau.fi/util v0.8.8/go.mod h1:Y/kS3loxTEhy8Vill513EtPXr+CRDdae+Xj2BXXMy/c= 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-20250717084138-aecc878ab213 h1:CYkW6OUQk0uvzMa3wN7RtGd7H8DxYdYqw/DoFobTK2w= -go.mau.fi/whatsmeow v0.0.0-20250717084138-aecc878ab213/go.mod h1:ltDTXUgOAT7LcFKp11H+5S7UY7+xHBMGzNJcv3dLHGk= +go.mau.fi/whatsmeow v0.0.0-20250722103218-f0d022d58b17 h1:oIqM1Rq1S8YlD76MeVlmv3C5RfA/w4PNPeGSOwhCLZg= +go.mau.fi/whatsmeow v0.0.0-20250722103218-f0d022d58b17/go.mod h1:ltDTXUgOAT7LcFKp11H+5S7UY7+xHBMGzNJcv3dLHGk= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= From 76079263d106ec26f858cf33481c655928dfba51 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 22 Jul 2025 15:58:13 +0300 Subject: [PATCH 067/276] handlewhatsapp: add support for lid avatar changes --- pkg/connector/handlewhatsapp.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index efd261d..beb360f 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -550,7 +550,7 @@ func (wa *WhatsAppClient) syncGhost(jid types.JID, reason string, pictureID *str Str("picture_id", ptr.Val(pictureID)). Stringer("jid", jid). Logger() - ctx := log.WithContext(context.Background()) + ctx := log.WithContext(wa.Main.Bridge.BackgroundCtx) ghost, err := wa.Main.Bridge.GetGhostByID(ctx, waid.MakeUserID(jid)) if err != nil { log.Err(err).Msg("Failed to get ghost") @@ -567,10 +567,22 @@ func (wa *WhatsAppClient) syncGhost(jid types.JID, reason string, pictureID *str log.Debug().Msg("Synced ghost info") } go wa.syncRemoteProfile(ctx, ghost) + var altJID types.JID + if jid.Server == types.HiddenUserServer { + altJID, err = wa.Device.LIDs.GetPNForLID(ctx, jid) + } else if jid.Server == types.DefaultUserServer { + altJID, err = wa.Device.LIDs.GetLIDForPN(ctx, jid) + } + if err != nil { + log.Warn().Err(err).Msg("Failed to get alternate JID for avatar change event") + } + if !altJID.IsEmpty() { + wa.syncGhost(altJID, reason+" + alt jid", pictureID) + } } func (wa *WhatsAppClient) handleWAPictureUpdate(ctx context.Context, evt *events.Picture) bool { - if evt.JID.Server == types.DefaultUserServer || evt.JID.Server == types.BotServer { + if evt.JID.Server == types.DefaultUserServer || evt.JID.Server == types.HiddenUserServer || evt.JID.Server == types.BotServer { go wa.syncGhost(evt.JID, "picture event", &evt.PictureID) return true } else { From 0be2c5e3b68762d20fac5962b67458b252a5571c Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 22 Jul 2025 16:21:54 +0300 Subject: [PATCH 068/276] backfill: mark task as not done when receiving new messages --- go.mod | 2 +- go.sum | 4 ++-- pkg/connector/backfill.go | 7 +++++++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 53354e5..0014f8e 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( golang.org/x/sync v0.16.0 google.golang.org/protobuf v1.36.6 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.24.2 + maunium.net/go/mautrix v0.24.3-0.20250722131825-3ecdb886bfd0 ) require ( diff --git a/go.sum b/go.sum index a057254..6b26e2e 100644 --- a/go.sum +++ b/go.sum @@ -101,5 +101,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.24.2 h1:+AVT5kbcA/QuT5svrJKp4ivwoUmz+RRplMp3DnfpheI= -maunium.net/go/mautrix v0.24.2/go.mod h1:1ut900w++eE9by9yqCR2dQdMqwsHwZG5L+1bKB1EvSA= +maunium.net/go/mautrix v0.24.3-0.20250722131825-3ecdb886bfd0 h1:9IgrNVTJlZVrTkqc8Ln2h+oxirCtStDiHy45ja+998I= +maunium.net/go/mautrix v0.24.3-0.20250722131825-3ecdb886bfd0/go.mod h1:1ut900w++eE9by9yqCR2dQdMqwsHwZG5L+1bKB1EvSA= diff --git a/pkg/connector/backfill.go b/pkg/connector/backfill.go index 81346b9..a9a0564 100644 --- a/pkg/connector/backfill.go +++ b/pkg/connector/backfill.go @@ -253,6 +253,13 @@ func (wa *WhatsAppClient) handleWAHistorySync(ctx context.Context, evt *waHistor } else { successfullySavedTotal += len(messages) } + err = wa.Main.Bridge.DB.BackfillTask.MarkNotDone(ctx, wa.makeWAPortalKey(jid), wa.UserLogin.ID) + if err != nil { + if stopOnError { + 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") + } } } log.Info(). From 70195c8d22027006a24501e7ea541780d0d8a911 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 22 Jul 2025 16:33:26 +0300 Subject: [PATCH 069/276] backfill,wadb: store login ts when chat was synced instead of boolean flag --- pkg/connector/backfill.go | 6 ++-- pkg/connector/login.go | 2 ++ pkg/connector/wadb/conversation.go | 31 +++++++++---------- .../wadb/upgrades/00-latest-schema.sql | 5 ++- .../upgrades/06-conversation-bridged-ts.sql | 4 +++ pkg/waid/dbmeta.go | 1 + 6 files changed, 28 insertions(+), 21 deletions(-) create mode 100644 pkg/connector/wadb/upgrades/06-conversation-bridged-ts.sql diff --git a/pkg/connector/backfill.go b/pkg/connector/backfill.go index a9a0564..6952f9c 100644 --- a/pkg/connector/backfill.go +++ b/pkg/connector/backfill.go @@ -277,7 +277,8 @@ func (wa *WhatsAppClient) createPortalsFromHistorySync(ctx context.Context) { Logger() ctx = log.WithContext(ctx) limit := wa.Main.Config.HistorySync.MaxInitialConversations - conversations, err := wa.Main.DB.Conversation.GetRecent(ctx, wa.UserLogin.ID, limit) + loginTS := wa.UserLogin.Metadata.(*waid.UserLoginMetadata).LoggedInAt + conversations, err := wa.Main.DB.Conversation.GetRecent(ctx, wa.UserLogin.ID, limit, loginTS) if err != nil { log.Err(err).Msg("Failed to get recent conversations from database") return @@ -285,6 +286,7 @@ func (wa *WhatsAppClient) createPortalsFromHistorySync(ctx context.Context) { log.Info(). Int("limit", limit). Int("conversation_count", len(conversations)). + Int64("login_timestamp", loginTS.Unix()). Msg("Creating portals from history sync") rateLimitErrors := 0 var wg sync.WaitGroup @@ -356,7 +358,7 @@ func (wa *WhatsAppClient) createPortalsFromHistorySync(ctx context.Context) { PortalKey: wa.makeWAPortalKey(conv.ChatJID), CreatePortal: true, PostHandleFunc: func(ctx context.Context, portal *bridgev2.Portal) { - err := wa.Main.DB.Conversation.MarkBridged(ctx, wa.UserLogin.ID, conv.ChatJID) + err := wa.Main.DB.Conversation.MarkSynced(ctx, wa.UserLogin.ID, conv.ChatJID, loginTS) if err != nil { zerolog.Ctx(ctx).Err(err).Msg("Failed to mark conversation as bridged") } diff --git a/pkg/connector/login.go b/pkg/connector/login.go index 5e8784e..ebf3060 100644 --- a/pkg/connector/login.go +++ b/pkg/connector/login.go @@ -9,6 +9,7 @@ import ( "github.com/rs/zerolog" "go.mau.fi/util/exsync" + "go.mau.fi/util/jsontime" "go.mau.fi/whatsmeow" "go.mau.fi/whatsmeow/types/events" waLog "go.mau.fi/whatsmeow/util/log" @@ -322,6 +323,7 @@ func (wl *WALogin) Wait(ctx context.Context) (*bridgev2.LoginStep, error) { }, Metadata: &waid.UserLoginMetadata{ WADeviceID: wl.LoginSuccess.ID.Device, + LoggedInAt: jsontime.UnixNow(), Timezone: wl.Timezone, HistorySyncPortalsNeedCreating: true, diff --git a/pkg/connector/wadb/conversation.go b/pkg/connector/wadb/conversation.go index 29694d7..85c7f44 100644 --- a/pkg/connector/wadb/conversation.go +++ b/pkg/connector/wadb/conversation.go @@ -6,6 +6,7 @@ import ( "time" "go.mau.fi/util/dbutil" + "go.mau.fi/util/jsontime" "go.mau.fi/util/ptr" "go.mau.fi/whatsmeow/proto/waHistorySync" "go.mau.fi/whatsmeow/types" @@ -30,7 +31,6 @@ type Conversation struct { EphemeralSettingTimestamp *int64 MarkedAsUnread *bool UnreadCount *uint32 - Bridged bool } func parseHistoryTime(ts *uint64) time.Time { @@ -69,9 +69,9 @@ const ( INSERT INTO whatsapp_history_sync_conversation ( bridge_id, user_login_id, chat_jid, last_message_timestamp, archived, pinned, mute_end_time, end_of_history_transfer_type, ephemeral_expiration, ephemeral_setting_timestamp, marked_as_unread, - unread_count, bridged + unread_count ) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12) ON CONFLICT (bridge_id, user_login_id, chat_jid) DO UPDATE SET last_message_timestamp=CASE @@ -87,16 +87,15 @@ const ( ephemeral_expiration=COALESCE(excluded.ephemeral_expiration, whatsapp_history_sync_conversation.ephemeral_expiration), ephemeral_setting_timestamp=COALESCE(excluded.ephemeral_setting_timestamp, whatsapp_history_sync_conversation.ephemeral_setting_timestamp), marked_as_unread=COALESCE(excluded.marked_as_unread, whatsapp_history_sync_conversation.marked_as_unread), - unread_count=COALESCE(excluded.unread_count, whatsapp_history_sync_conversation.unread_count), - bridged=false + unread_count=COALESCE(excluded.unread_count, whatsapp_history_sync_conversation.unread_count) ` getRecentConversations = ` SELECT bridge_id, user_login_id, chat_jid, last_message_timestamp, archived, pinned, mute_end_time, end_of_history_transfer_type, ephemeral_expiration, ephemeral_setting_timestamp, marked_as_unread, - unread_count, bridged + unread_count FROM whatsapp_history_sync_conversation - WHERE bridge_id=$1 AND user_login_id=$2 AND bridged=false + WHERE bridge_id=$1 AND user_login_id=$2 AND (synced_login_ts IS NULL OR synced_login_ts < $4) ORDER BY last_message_timestamp DESC LIMIT $3 ` @@ -104,7 +103,7 @@ const ( SELECT bridge_id, user_login_id, chat_jid, last_message_timestamp, archived, pinned, mute_end_time, end_of_history_transfer_type, ephemeral_expiration, ephemeral_setting_timestamp, marked_as_unread, - unread_count, bridged + unread_count FROM whatsapp_history_sync_conversation WHERE bridge_id=$1 AND user_login_id=$2 AND chat_jid=$3 ` @@ -113,9 +112,9 @@ const ( DELETE FROM whatsapp_history_sync_conversation WHERE bridge_id=$1 AND user_login_id=$2 AND chat_jid=$3 ` - markConversationBridged = ` + markConversationSynced = ` UPDATE whatsapp_history_sync_conversation - SET bridged=true + SET synced_login_ts=$4 WHERE bridge_id=$1 AND user_login_id=$2 AND chat_jid=$3 ` ) @@ -125,17 +124,19 @@ func (cq *ConversationQuery) Put(ctx context.Context, conv *Conversation) error return cq.Exec(ctx, upsertHistorySyncConversationQuery, conv.sqlVariables()...) } -func (cq *ConversationQuery) GetRecent(ctx context.Context, loginID networkid.UserLoginID, limit int) ([]*Conversation, error) { +func (cq *ConversationQuery) GetRecent( + ctx context.Context, loginID networkid.UserLoginID, limit int, notSyncedAfter jsontime.Unix, +) ([]*Conversation, error) { limitPtr := &limit // Negative limit on SQLite means unlimited, but Postgres prefers a NULL limit. if limit < 0 && cq.GetDB().Dialect == dbutil.Postgres { limitPtr = nil } - return cq.QueryMany(ctx, getRecentConversations, cq.BridgeID, loginID, limitPtr) + return cq.QueryMany(ctx, getRecentConversations, cq.BridgeID, loginID, limitPtr, notSyncedAfter) } -func (cq *ConversationQuery) MarkBridged(ctx context.Context, loginID networkid.UserLoginID, chatJID types.JID) error { - return cq.Exec(ctx, markConversationBridged, cq.BridgeID, loginID, chatJID) +func (cq *ConversationQuery) MarkSynced(ctx context.Context, loginID networkid.UserLoginID, chatJID types.JID, loginTS jsontime.Unix) error { + return cq.Exec(ctx, markConversationSynced, cq.BridgeID, loginID, chatJID, loginTS) } func (cq *ConversationQuery) Get(ctx context.Context, loginID networkid.UserLoginID, chatJID types.JID) (*Conversation, error) { @@ -171,7 +172,6 @@ func (c *Conversation) sqlVariables() []any { c.EphemeralSettingTimestamp, c.MarkedAsUnread, c.UnreadCount, - c.Bridged, } } @@ -190,7 +190,6 @@ func (c *Conversation) Scan(row dbutil.Scannable) (*Conversation, error) { &c.EphemeralSettingTimestamp, &c.MarkedAsUnread, &c.UnreadCount, - &c.Bridged, ) if err != nil { return nil, err diff --git a/pkg/connector/wadb/upgrades/00-latest-schema.sql b/pkg/connector/wadb/upgrades/00-latest-schema.sql index f99bad4..59baff4 100644 --- a/pkg/connector/wadb/upgrades/00-latest-schema.sql +++ b/pkg/connector/wadb/upgrades/00-latest-schema.sql @@ -1,4 +1,4 @@ --- v0 -> v5 (compatible with v3+): Latest revision +-- v0 -> v6 (compatible with v3+): Latest revision CREATE TABLE whatsapp_poll_option_id ( bridge_id TEXT NOT NULL, @@ -26,8 +26,7 @@ CREATE TABLE whatsapp_history_sync_conversation ( ephemeral_setting_timestamp BIGINT, marked_as_unread BOOLEAN, unread_count INTEGER, - - bridged BOOLEAN NOT NULL DEFAULT false, + synced_login_ts BIGINT, PRIMARY KEY (bridge_id, user_login_id, chat_jid), CONSTRAINT whatsapp_history_sync_conversation_user_login_fkey FOREIGN KEY (bridge_id, user_login_id) diff --git a/pkg/connector/wadb/upgrades/06-conversation-bridged-ts.sql b/pkg/connector/wadb/upgrades/06-conversation-bridged-ts.sql new file mode 100644 index 0000000..8d8001b --- /dev/null +++ b/pkg/connector/wadb/upgrades/06-conversation-bridged-ts.sql @@ -0,0 +1,4 @@ +-- v6 (compatible with v3+): Store timestamp for which login a conversation was synced with +ALTER TABLE whatsapp_history_sync_conversation ADD COLUMN synced_login_ts BIGINT; +UPDATE whatsapp_history_sync_conversation SET synced_login_ts = 0 WHERE bridged = true; +ALTER TABLE whatsapp_history_sync_conversation DROP COLUMN bridged; diff --git a/pkg/waid/dbmeta.go b/pkg/waid/dbmeta.go index c67f587..58b1521 100644 --- a/pkg/waid/dbmeta.go +++ b/pkg/waid/dbmeta.go @@ -35,6 +35,7 @@ type UserLoginMetadata struct { PushKeys *PushKeys `json:"push_keys,omitempty"` APNSEncPubKey []byte `json:"apns_enc_pubkey,omitempty"` APNSEncPrivKey []byte `json:"apns_enc_privkey,omitempty"` + LoggedInAt jsontime.Unix `json:"logged_in_at,omitempty"` HistorySyncPortalsNeedCreating bool `json:"history_sync_portals_need_creating,omitempty"` } From 8b36b353cfd96109538c653d190035656a9f9717 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 22 Jul 2025 17:53:05 +0300 Subject: [PATCH 070/276] backfill: make wait time before starting portal creation configurable --- pkg/connector/backfill.go | 78 +++++++++++++++++++------------ pkg/connector/config.go | 7 ++- pkg/connector/example-config.yaml | 4 ++ 3 files changed, 58 insertions(+), 31 deletions(-) diff --git a/pkg/connector/backfill.go b/pkg/connector/backfill.go index 6952f9c..392fcea 100644 --- a/pkg/connector/backfill.go +++ b/pkg/connector/backfill.go @@ -8,6 +8,7 @@ import ( "strconv" "strings" "sync" + "sync/atomic" "time" "github.com/rs/zerolog" @@ -28,13 +29,13 @@ import ( var _ bridgev2.BackfillingNetworkAPI = (*WhatsAppClient)(nil) -const historySyncDispatchWait = 30 * time.Second - func (wa *WhatsAppClient) historySyncLoop(ctx context.Context) { - dispatchTimer := time.NewTimer(historySyncDispatchWait) + dispatchTimer := time.NewTimer(wa.Main.Config.HistorySync.DispatchWait) + var timerPending atomic.Bool if !wa.isNewLogin && wa.UserLogin.Metadata.(*waid.UserLoginMetadata).HistorySyncPortalsNeedCreating { dispatchTimer.Reset(5 * time.Second) + timerPending.Store(true) } else { dispatchTimer.Stop() } @@ -45,35 +46,52 @@ func (wa *WhatsAppClient) historySyncLoop(ctx context.Context) { default: } } - wa.UserLogin.Log.Debug().Msg("Starting history sync loop") + wa.UserLogin.Log.Debug().Msg("Starting history sync loops") + // Separate loop for creating portals to ensure it doesn't block processing new history sync payloads. + go func() { + for { + select { + case <-dispatchTimer.C: + timerPending.Store(false) + wa.createPortalsFromHistorySync(ctx) + case <-ctx.Done(): + wa.UserLogin.Log.Debug().Msg("Stopping portal creation history sync loop") + return + } + } + }() for { + var resetTimer bool + // 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() select { case evt := <-wa.historySyncs: - dispatchTimer.Stop() - _ = wa.handleWAHistorySync(ctx, evt, false) - dispatchTimer.Reset(historySyncDispatchWait) + resetTimer, _ = wa.handleWAHistorySync(ctx, evt, false) case <-wa.historySyncWakeup: - dispatchTimer.Stop() notif, rowid, err := wa.Main.DB.HSNotif.GetNext(ctx, wa.UserLogin.ID) if err != nil { wa.UserLogin.Log.Err(err).Msg("Failed to get next history sync notification") } else if notif == nil { wa.UserLogin.Log.Debug().Msg("No more queued history sync notifications") } else { - wa.downloadAndSaveWAHistorySyncData(ctx, notif, rowid) + resetTimer = wa.downloadAndSaveWAHistorySyncData(ctx, notif, rowid) // Continue waking up the loop until all queued notifications are processed select { case wa.historySyncWakeup <- struct{}{}: default: } } - dispatchTimer.Reset(historySyncDispatchWait) - case <-dispatchTimer.C: - wa.createPortalsFromHistorySync(ctx) case <-ctx.Done(): - wa.UserLogin.Log.Debug().Msg("Stopping history sync loop") + wa.UserLogin.Log.Debug().Msg("Stopping main history sync loop") return } + if resetTimer { + timerPending.Store(true) + } + if timerPending.Load() { + dispatchTimer.Reset(wa.Main.Config.HistorySync.DispatchWait) + } } } @@ -94,7 +112,7 @@ func (wa *WhatsAppClient) saveWAHistorySyncNotification(ctx context.Context, evt } } -func (wa *WhatsAppClient) downloadAndSaveWAHistorySyncData(ctx context.Context, evt *waE2E.HistorySyncNotification, rowid int) { +func (wa *WhatsAppClient) downloadAndSaveWAHistorySyncData(ctx context.Context, evt *waE2E.HistorySyncNotification, rowid int) (resetTimer bool) { log := wa.UserLogin.Log.With(). Str("action", "download history sync"). Stringer("sync_type", evt.GetSyncType()). @@ -107,25 +125,26 @@ func (wa *WhatsAppClient) downloadAndSaveWAHistorySyncData(ctx context.Context, log.Err(err).Msg("Failed to download history sync") return } - err = wa.Main.DB.DoTxn(ctx, nil, func(ctx context.Context) error { - err := wa.handleWAHistorySync(ctx, blob, true) - if err != nil { - return err + err = wa.Main.DB.DoTxn(ctx, nil, func(ctx context.Context) (innerErr error) { + resetTimer, innerErr = wa.handleWAHistorySync(ctx, blob, true) + if innerErr != nil { + return } - err = wa.Main.DB.HSNotif.Delete(ctx, rowid) - if err != nil { - return fmt.Errorf("failed to delete queued history sync notification: %w", err) + innerErr = wa.Main.DB.HSNotif.Delete(ctx, rowid) + if innerErr != nil { + innerErr = fmt.Errorf("failed to delete queued history sync notification: %w", innerErr) } - return nil + return }) if err != nil { log.Err(err).Msg("Failed to store history sync notification data") } + return } -func (wa *WhatsAppClient) handleWAHistorySync(ctx context.Context, evt *waHistorySync.HistorySync, stopOnError bool) error { +func (wa *WhatsAppClient) handleWAHistorySync(ctx context.Context, evt *waHistorySync.HistorySync, stopOnError bool) (bool, error) { if evt == nil || evt.SyncType == nil { - return nil + return false, nil } log := wa.UserLogin.Log.With(). Str("action", "store history sync"). @@ -145,7 +164,7 @@ func (wa *WhatsAppClient) handleWAHistorySync(ctx context.Context, evt *waHistor Int("recent_sticker_count", len(evt.GetRecentStickers())). Int("past_participant_count", len(evt.GetPastParticipants())). Msg("Ignoring history sync") - return nil + return false, nil } log.Info(). Int("conversation_count", len(evt.GetConversations())). @@ -238,7 +257,7 @@ func (wa *WhatsAppClient) handleWAHistorySync(ctx context.Context, evt *waHistor err = wa.Main.DB.Conversation.Put(ctx, wadb.NewConversation(wa.UserLogin.ID, jid, conv, maxTime)) if err != nil { if stopOnError { - return fmt.Errorf("failed to save conversation metadata for %s: %w", jid, err) + return false, fmt.Errorf("failed to save conversation metadata for %s: %w", jid, err) } log.Err(err).Msg("Failed to save conversation metadata") continue @@ -246,7 +265,7 @@ func (wa *WhatsAppClient) handleWAHistorySync(ctx context.Context, evt *waHistor err = wa.Main.DB.Message.Put(ctx, wa.UserLogin.ID, jid, messages) if err != nil { if stopOnError { - return fmt.Errorf("failed to save messages in %s: %w", jid, err) + return false, fmt.Errorf("failed to save messages in %s: %w", jid, err) } log.Err(err).Msg("Failed to save messages") failedToSaveTotal += len(messages) @@ -256,7 +275,7 @@ func (wa *WhatsAppClient) handleWAHistorySync(ctx context.Context, evt *waHistor err = wa.Main.Bridge.DB.BackfillTask.MarkNotDone(ctx, wa.makeWAPortalKey(jid), wa.UserLogin.ID) if err != nil { if stopOnError { - return fmt.Errorf("failed to mark backfill task as not done for %s: %w", jid, err) + return false, 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") } @@ -268,7 +287,8 @@ func (wa *WhatsAppClient) handleWAHistorySync(ctx context.Context, evt *waHistor Int("total_message_count", totalMessageCount). Dur("duration", time.Since(start)). Msg("Finished storing history sync") - return nil + resetTimer := evt.GetSyncType() == waHistorySync.HistorySync_RECENT || evt.GetSyncType() == waHistorySync.HistorySync_FULL + return resetTimer, nil } func (wa *WhatsAppClient) createPortalsFromHistorySync(ctx context.Context) { diff --git a/pkg/connector/config.go b/pkg/connector/config.go index e5a9209..fd6ac0b 100644 --- a/pkg/connector/config.go +++ b/pkg/connector/config.go @@ -4,6 +4,7 @@ import ( _ "embed" "strings" "text/template" + "time" up "go.mau.fi/util/configupgrade" "go.mau.fi/whatsmeow/types" @@ -53,8 +54,9 @@ type Config struct { AnimatedSticker msgconv.AnimatedStickerConfig `yaml:"animated_sticker"` HistorySync struct { - MaxInitialConversations int `yaml:"max_initial_conversations"` - RequestFullSync bool `yaml:"request_full_sync"` + MaxInitialConversations int `yaml:"max_initial_conversations"` + RequestFullSync bool `yaml:"request_full_sync"` + DispatchWait time.Duration `yaml:"dispatch_wait"` FullSyncConfig struct { DaysLimit uint32 `yaml:"days_limit"` SizeLimit uint32 `yaml:"size_mb_limit"` @@ -122,6 +124,7 @@ func upgradeConfig(helper up.Helper) { helper.Copy(up.Int, "history_sync", "max_initial_conversations") helper.Copy(up.Bool, "history_sync", "request_full_sync") + helper.Copy(up.Str|up.Int, "history_sync", "dispatch_wait") helper.Copy(up.Int|up.Null, "history_sync", "full_sync_config", "days_limit") helper.Copy(up.Int|up.Null, "history_sync", "full_sync_config", "size_mb_limit") helper.Copy(up.Int|up.Null, "history_sync", "full_sync_config", "storage_quota_mb") diff --git a/pkg/connector/example-config.yaml b/pkg/connector/example-config.yaml index 37c41f7..6b9e2e4 100644 --- a/pkg/connector/example-config.yaml +++ b/pkg/connector/example-config.yaml @@ -88,6 +88,10 @@ history_sync: # Should the bridge request a full sync from the phone when logging in? # This bumps the size of history syncs from 3 months to 1 year. request_full_sync: false + # Time to wait for history sync payloads before starting backfill. Each new payload resets the timer. + # If this is too low, the backfill may happen with incomplete history + # and backfill less messages than what is configured in the backfill section. + dispatch_wait: 1m # Configuration parameters that are sent to the phone along with the request full sync flag. # By default, (when the values are null or 0), the config isn't sent at all. full_sync_config: From b4f42f2e9cc73563b1e9ea9190355c5a55e50b19 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 22 Jul 2025 17:57:56 +0300 Subject: [PATCH 071/276] userinfo: ensure lid/pn ghost is always synced when the other one is --- pkg/connector/handlewhatsapp.go | 13 +---------- pkg/connector/userinfo.go | 38 ++++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index beb360f..330bea0 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -565,20 +565,9 @@ func (wa *WhatsAppClient) syncGhost(jid types.JID, reason string, pictureID *str } else { ghost.UpdateInfo(ctx, userInfo) log.Debug().Msg("Synced ghost info") + wa.syncAltGhostWithInfo(ctx, jid, userInfo) } go wa.syncRemoteProfile(ctx, ghost) - var altJID types.JID - if jid.Server == types.HiddenUserServer { - altJID, err = wa.Device.LIDs.GetPNForLID(ctx, jid) - } else if jid.Server == types.DefaultUserServer { - altJID, err = wa.Device.LIDs.GetLIDForPN(ctx, jid) - } - if err != nil { - log.Warn().Err(err).Msg("Failed to get alternate JID for avatar change event") - } - if !altJID.IsEmpty() { - wa.syncGhost(altJID, reason+" + alt jid", pictureID) - } } func (wa *WhatsAppClient) handleWAPictureUpdate(ctx context.Context, evt *events.Picture) bool { diff --git a/pkg/connector/userinfo.go b/pkg/connector/userinfo.go index 4d7d982..ce4f360 100644 --- a/pkg/connector/userinfo.go +++ b/pkg/connector/userinfo.go @@ -161,6 +161,7 @@ func (wa *WhatsAppClient) doGhostResync(ctx context.Context, queue map[types.JID continue } ghost.UpdateInfo(ctx, userInfo) + wa.syncAltGhostWithInfo(ctx, jid, userInfo) } } @@ -289,7 +290,42 @@ func (wa *WhatsAppClient) resyncContacts(forceAvatarSync bool) { if err != nil { log.Err(err).Msg("Failed to get ghost") } else if ghost != nil { - ghost.UpdateInfo(ctx, wa.contactToUserInfo(ctx, jid, contact, forceAvatarSync || ghost.AvatarID == "")) + userInfo := wa.contactToUserInfo(ctx, jid, contact, forceAvatarSync || ghost.AvatarID == "") + ghost.UpdateInfo(ctx, userInfo) + wa.syncAltGhostWithInfo(ctx, jid, userInfo) } } } + +func (wa *WhatsAppClient) syncAltGhostWithInfo(ctx context.Context, jid types.JID, info *bridgev2.UserInfo) { + log := zerolog.Ctx(ctx) + var altJID types.JID + var err error + if jid.Server == types.HiddenUserServer { + altJID, err = wa.Device.LIDs.GetPNForLID(ctx, jid) + } else if jid.Server == types.DefaultUserServer { + altJID, err = wa.Device.LIDs.GetLIDForPN(ctx, jid) + } + if err != nil { + log.Warn().Err(err). + Stringer("jid", jid). + Msg("Failed to get alternate JID for syncing user info") + return + } else if altJID.IsEmpty() { + return + } + ghost, err := wa.Main.Bridge.GetGhostByID(ctx, waid.MakeUserID(altJID)) + if err != nil { + log.Err(err). + Stringer("alternate_jid", altJID). + Stringer("jid", jid). + Msg("Failed to get ghost for alternate JID") + return + } + ghost.UpdateInfo(ctx, info) + log.Debug(). + Stringer("jid", jid). + Stringer("alternate_jid", altJID). + Msg("Synced alternate ghost with info") + go wa.syncRemoteProfile(ctx, ghost) +} From 1b6ed76fe9df8a64ed6e19233b9f13d694b97770 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 22 Jul 2025 19:12:07 +0300 Subject: [PATCH 072/276] backfill: fix timer stopping --- pkg/connector/backfill.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pkg/connector/backfill.go b/pkg/connector/backfill.go index 392fcea..bebce98 100644 --- a/pkg/connector/backfill.go +++ b/pkg/connector/backfill.go @@ -62,13 +62,14 @@ func (wa *WhatsAppClient) historySyncLoop(ctx context.Context) { }() for { var resetTimer bool - // 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() 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) if err != nil { wa.UserLogin.Log.Err(err).Msg("Failed to get next history sync notification") @@ -287,7 +288,8 @@ func (wa *WhatsAppClient) handleWAHistorySync(ctx context.Context, evt *waHistor 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 + resetTimer := evt.GetSyncType() == waHistorySync.HistorySync_RECENT || + evt.GetSyncType() == waHistorySync.HistorySync_FULL return resetTimer, nil } From e103a8bee4da94669ca555737d30949f3f3551a2 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 22 Jul 2025 19:25:21 +0300 Subject: [PATCH 073/276] backfill: fix ignoring PSA chat --- go.mod | 2 +- go.sum | 4 ++-- pkg/connector/backfill.go | 2 +- pkg/connector/userinfo.go | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 0014f8e..43f795b 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.8.8 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250722103218-f0d022d58b17 + go.mau.fi/whatsmeow v0.0.0-20250722162452-75177ce21187 golang.org/x/image v0.29.0 golang.org/x/net v0.42.0 golang.org/x/sync v0.16.0 diff --git a/go.sum b/go.sum index 6b26e2e..d2ea53c 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ go.mau.fi/util v0.8.8 h1:OnuEEc/sIJFhnq4kFggiImUpcmnmL/xpvQMRu5Fiy5c= go.mau.fi/util v0.8.8/go.mod h1:Y/kS3loxTEhy8Vill513EtPXr+CRDdae+Xj2BXXMy/c= 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-20250722103218-f0d022d58b17 h1:oIqM1Rq1S8YlD76MeVlmv3C5RfA/w4PNPeGSOwhCLZg= -go.mau.fi/whatsmeow v0.0.0-20250722103218-f0d022d58b17/go.mod h1:ltDTXUgOAT7LcFKp11H+5S7UY7+xHBMGzNJcv3dLHGk= +go.mau.fi/whatsmeow v0.0.0-20250722162452-75177ce21187 h1:3gLaJu5UhCIoD+FpcpfPrqopRSu90PVyei685CifCYY= +go.mau.fi/whatsmeow v0.0.0-20250722162452-75177ce21187/go.mod h1:ltDTXUgOAT7LcFKp11H+5S7UY7+xHBMGzNJcv3dLHGk= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= diff --git a/pkg/connector/backfill.go b/pkg/connector/backfill.go index bebce98..78a5a4f 100644 --- a/pkg/connector/backfill.go +++ b/pkg/connector/backfill.go @@ -325,7 +325,7 @@ func (wa *WhatsAppClient) createPortalsFromHistorySync(ctx context.Context) { if conv.ChatJID == types.StatusBroadcastJID && !wa.Main.Config.EnableStatusBroadcast { wg.Done() continue - } else if conv.ChatJID == types.PSAJID { + } else if conv.ChatJID == types.PSAJID || conv.ChatJID == types.LegacyPSAJID { // We don't currently support new PSAs, so don't bother backfilling them either wg.Done() continue diff --git a/pkg/connector/userinfo.go b/pkg/connector/userinfo.go index ce4f360..0a25d48 100644 --- a/pkg/connector/userinfo.go +++ b/pkg/connector/userinfo.go @@ -185,7 +185,7 @@ func (wa *WhatsAppClient) getUserInfo(ctx context.Context, jid types.JID, fetchA func (wa *WhatsAppClient) contactToUserInfo(ctx context.Context, jid types.JID, contact types.ContactInfo, getAvatar bool) *bridgev2.UserInfo { if jid == types.MetaAIJID && contact.PushName == jid.User { contact.PushName = "Meta AI" - } else if jid == types.PSAJID { + } else if jid == types.LegacyPSAJID || jid == types.PSAJID { contact.PushName = "WhatsApp" } var phone string From ebeef0bbbb41ac6ee7ca31f8908a0205445c8794 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 22 Jul 2025 20:33:29 +0300 Subject: [PATCH 074/276] 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 43f795b..4785e41 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( golang.org/x/sync v0.16.0 google.golang.org/protobuf v1.36.6 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.24.3-0.20250722131825-3ecdb886bfd0 + maunium.net/go/mautrix v0.24.3-0.20250722173131-cb80e5c63f7f ) require ( diff --git a/go.sum b/go.sum index d2ea53c..e213289 100644 --- a/go.sum +++ b/go.sum @@ -101,5 +101,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.24.3-0.20250722131825-3ecdb886bfd0 h1:9IgrNVTJlZVrTkqc8Ln2h+oxirCtStDiHy45ja+998I= -maunium.net/go/mautrix v0.24.3-0.20250722131825-3ecdb886bfd0/go.mod h1:1ut900w++eE9by9yqCR2dQdMqwsHwZG5L+1bKB1EvSA= +maunium.net/go/mautrix v0.24.3-0.20250722173131-cb80e5c63f7f h1:GeptxClgWaqGyRMOPh7beZHcySmyBMpRsaOCU7ZkKWg= +maunium.net/go/mautrix v0.24.3-0.20250722173131-cb80e5c63f7f/go.mod h1:1ut900w++eE9by9yqCR2dQdMqwsHwZG5L+1bKB1EvSA= From a2275a587f48faa1f498e589e68379c9c4a9e6b6 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 22 Jul 2025 22:45:54 +0300 Subject: [PATCH 075/276] 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 4785e41..71cb34a 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.8.8 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250722162452-75177ce21187 + go.mau.fi/whatsmeow v0.0.0-20250722194234-b61df67bf925 golang.org/x/image v0.29.0 golang.org/x/net v0.42.0 golang.org/x/sync v0.16.0 diff --git a/go.sum b/go.sum index e213289..e300c40 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ go.mau.fi/util v0.8.8 h1:OnuEEc/sIJFhnq4kFggiImUpcmnmL/xpvQMRu5Fiy5c= go.mau.fi/util v0.8.8/go.mod h1:Y/kS3loxTEhy8Vill513EtPXr+CRDdae+Xj2BXXMy/c= 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-20250722162452-75177ce21187 h1:3gLaJu5UhCIoD+FpcpfPrqopRSu90PVyei685CifCYY= -go.mau.fi/whatsmeow v0.0.0-20250722162452-75177ce21187/go.mod h1:ltDTXUgOAT7LcFKp11H+5S7UY7+xHBMGzNJcv3dLHGk= +go.mau.fi/whatsmeow v0.0.0-20250722194234-b61df67bf925 h1:GxOYzZ6x/mRtuIx/ijDy5bGzQkRW8grSET51OfGA1bk= +go.mau.fi/whatsmeow v0.0.0-20250722194234-b61df67bf925/go.mod h1:ltDTXUgOAT7LcFKp11H+5S7UY7+xHBMGzNJcv3dLHGk= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= From 2ae35f03778d1cf8dd98e4db18c2c026d336fbfe Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 22 Jul 2025 23:10:34 +0300 Subject: [PATCH 076/276] handlewhatsapp: route bot messages to phone number DM as well --- go.mod | 2 +- go.sum | 4 ++-- pkg/connector/handlewhatsapp.go | 15 +++++++++++++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 71cb34a..e99ec00 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( golang.org/x/sync v0.16.0 google.golang.org/protobuf v1.36.6 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.24.3-0.20250722173131-cb80e5c63f7f + maunium.net/go/mautrix v0.24.3-0.20250722203558-463d2ea6d011 ) require ( diff --git a/go.sum b/go.sum index e300c40..26a8122 100644 --- a/go.sum +++ b/go.sum @@ -101,5 +101,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.24.3-0.20250722173131-cb80e5c63f7f h1:GeptxClgWaqGyRMOPh7beZHcySmyBMpRsaOCU7ZkKWg= -maunium.net/go/mautrix v0.24.3-0.20250722173131-cb80e5c63f7f/go.mod h1:1ut900w++eE9by9yqCR2dQdMqwsHwZG5L+1bKB1EvSA= +maunium.net/go/mautrix v0.24.3-0.20250722203558-463d2ea6d011 h1:s83n+xEOe6iv6K430pwKc4TkId8AJ8DuwvoEIWKEj0o= +maunium.net/go/mautrix v0.24.3-0.20250722203558-463d2ea6d011/go.mod h1:1ut900w++eE9by9yqCR2dQdMqwsHwZG5L+1bKB1EvSA= diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index 330bea0..3386f99 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -260,6 +260,21 @@ func (wa *WhatsAppClient) handleWAMessage(ctx context.Context, evt *events.Messa Str("message_id", evt.Info.ID). Msg("Forced LID DM sender to phone number in own message sent from another device") evt.Info.Chat = evt.Info.RecipientAlt.ToNonAD() + } else if evt.Info.Sender.Server == types.BotServer && evt.Info.Chat.Server == types.HiddenUserServer { + chatPN, err := wa.Device.LIDs.GetPNForLID(ctx, evt.Info.Chat) + if err != nil { + wa.UserLogin.Log.Err(err). + Str("message_id", evt.Info.ID). + Stringer("lid", evt.Info.Chat). + Msg("Failed to get phone number of DM for incoming bot message") + } else if !chatPN.IsEmpty() { + wa.UserLogin.Log.Debug(). + Stringer("lid", evt.Info.Chat). + Stringer("pn", chatPN). + Str("message_id", evt.Info.ID). + Msg("Forced LID chat to phone number in bot message") + evt.Info.Chat = chatPN + } } wa.UserLogin.Log.Trace(). Any("info", evt.Info). From a26b8e72bb27339e7829c40d400cd4a38106e24c Mon Sep 17 00:00:00 2001 From: Kishan Bagaria <1093313+KishanBagaria@users.noreply.github.com> Date: Sun, 27 Jul 2025 19:58:55 +0530 Subject: [PATCH 077/276] client: translate viewing chat status into presence on whatsapp (#816) --- pkg/connector/client.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/pkg/connector/client.go b/pkg/connector/client.go index e1b01d3..122b393 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -112,6 +112,7 @@ type WhatsAppClient struct { mediaRetryLock *semaphore.Weighted offlineSyncWaiter chan error isNewLogin bool + lastPresence types.Presence } var ( @@ -387,6 +388,22 @@ func (wa *WhatsAppClient) syncRemoteProfile(ctx context.Context, ghost *bridgev2 } func (wa *WhatsAppClient) HandleMatrixViewingChat(ctx context.Context, msg *bridgev2.MatrixViewingChat) error { + var presence types.Presence + if msg.Portal != nil { + presence = types.PresenceAvailable + } else { + presence = types.PresenceUnavailable + } + + if wa.lastPresence != presence { + err := wa.Client.SendPresence(presence) + if err != nil { + zerolog.Ctx(ctx).Warn().Err(err).Msg("Failed to set presence when viewing chat") + } else { + wa.lastPresence = presence + } + } + if msg.Portal == nil || msg.Portal.Metadata.(*waid.PortalMetadata).LastSync.Add(5*time.Minute).After(time.Now()) { // If we resynced this portal within the last 5 minutes, don't do it again return nil From 5965ee828d575a98fb53c4091c895ef91689310e Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 28 Jul 2025 15:59:00 +0300 Subject: [PATCH 078/276] legacyprovision: switch to standard http mux --- cmd/mautrix-whatsapp/legacyprovision.go | 3 +-- cmd/mautrix-whatsapp/main.go | 12 +++++------- go.mod | 12 ++++++------ go.sum | 24 ++++++++++++------------ 4 files changed, 24 insertions(+), 27 deletions(-) diff --git a/cmd/mautrix-whatsapp/legacyprovision.go b/cmd/mautrix-whatsapp/legacyprovision.go index 4b4793c..169fa01 100644 --- a/cmd/mautrix-whatsapp/legacyprovision.go +++ b/cmd/mautrix-whatsapp/legacyprovision.go @@ -7,7 +7,6 @@ import ( "regexp" "strings" - "github.com/gorilla/mux" "github.com/gorilla/websocket" "github.com/rs/zerolog/hlog" "go.mau.fi/util/exhttp" @@ -263,7 +262,7 @@ func legacyProvContacts(w http.ResponseWriter, r *http.Request) { } func legacyProvResolveIdentifier(w http.ResponseWriter, r *http.Request) { - number := mux.Vars(r)["number"] + number := r.PathValue("number") userLogin := m.Matrix.Provisioning.GetLoginForRequest(w, r) if userLogin == nil { return diff --git a/cmd/mautrix-whatsapp/main.go b/cmd/mautrix-whatsapp/main.go index 0ec230f..fc0364d 100644 --- a/cmd/mautrix-whatsapp/main.go +++ b/cmd/mautrix-whatsapp/main.go @@ -1,8 +1,6 @@ package main import ( - "net/http" - "maunium.net/go/mautrix/bridgev2/bridgeconfig" "maunium.net/go/mautrix/bridgev2/matrix/mxmain" @@ -43,11 +41,11 @@ func main() { } m.PostStart = func() { if m.Matrix.Provisioning != nil { - m.Matrix.Provisioning.Router.HandleFunc("/v1/login", legacyProvLogin).Methods(http.MethodGet) - m.Matrix.Provisioning.Router.HandleFunc("/v1/logout", legacyProvLogout).Methods(http.MethodPost) - m.Matrix.Provisioning.Router.HandleFunc("/v1/contacts", legacyProvContacts).Methods(http.MethodGet) - m.Matrix.Provisioning.Router.HandleFunc("/v1/resolve_identifier/{number}", legacyProvResolveIdentifier).Methods(http.MethodGet) - m.Matrix.Provisioning.Router.HandleFunc("/v1/pm/{number}", legacyProvResolveIdentifier).Methods(http.MethodPost) + m.Matrix.Provisioning.Router.HandleFunc("GET /v1/login", legacyProvLogin) + m.Matrix.Provisioning.Router.HandleFunc("POST /v1/logout", legacyProvLogout) + 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.GetAuthFromRequest = legacyProvAuth } } diff --git a/go.mod b/go.mod index e99ec00..685af2e 100644 --- a/go.mod +++ b/go.mod @@ -5,30 +5,30 @@ go 1.23.0 toolchain go1.24.5 require ( - github.com/gorilla/mux v1.8.0 github.com/gorilla/websocket v1.5.0 github.com/lib/pq v1.10.9 github.com/rs/zerolog v1.34.0 - go.mau.fi/util v0.8.8 + go.mau.fi/util v0.8.9-0.20250723171559-474867266038 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250722194234-b61df67bf925 + go.mau.fi/whatsmeow v0.0.0-20250723082402-b6b13c69c9cb golang.org/x/image v0.29.0 golang.org/x/net v0.42.0 golang.org/x/sync v0.16.0 google.golang.org/protobuf v1.36.6 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.24.3-0.20250722203558-463d2ea6d011 + maunium.net/go/mautrix v0.24.3-0.20250728125317-74ab3b118e10 ) require ( filippo.io/edwards25519 v1.1.0 // indirect + github.com/coder/websocket v1.8.13 // indirect github.com/coreos/go-systemd/v22 v22.5.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.28 // indirect - github.com/petermattis/goid v0.0.0-20250508124226-395b08cebbdb // indirect + github.com/petermattis/goid v0.0.0-20250721140440-ea1c0173183e // 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,7 +40,7 @@ require ( go.mau.fi/libsignal v0.2.0 // indirect go.mau.fi/zeroconfig v0.1.3 // indirect golang.org/x/crypto v0.40.0 // indirect - golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc // indirect + golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 // indirect golang.org/x/sys v0.34.0 // indirect golang.org/x/text v0.27.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect diff --git a/go.sum b/go.sum index 26a8122..bcdbb97 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= 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/coder/websocket v1.8.13 h1:f3QZdXy7uGVz+4uCJy2nTZyM0yTBj8yANEHhqlXZ9FE= +github.com/coder/websocket v1.8.13/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= 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/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -12,8 +14,6 @@ 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/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/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= 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= @@ -34,8 +34,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.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A= github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/petermattis/goid v0.0.0-20250508124226-395b08cebbdb h1:3PrKuO92dUTMrQ9dx0YNejC6U/Si6jqKmyQ9vWjwqR4= -github.com/petermattis/goid v0.0.0-20250508124226-395b08cebbdb/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +github.com/petermattis/goid v0.0.0-20250721140440-ea1c0173183e h1:D0bJD+4O3G4izvrQUmzCL80zazlN7EwJ0PPDhpJWC/I= +github.com/petermattis/goid v0.0.0-20250721140440-ea1c0173183e/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= @@ -65,18 +65,18 @@ github.com/yuin/goldmark v1.7.12 h1:YwGP/rrea2/CnCtUHgjuolG/PnMxdQtPMO5PvaE2/nY= github.com/yuin/goldmark v1.7.12/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.8.8 h1:OnuEEc/sIJFhnq4kFggiImUpcmnmL/xpvQMRu5Fiy5c= -go.mau.fi/util v0.8.8/go.mod h1:Y/kS3loxTEhy8Vill513EtPXr+CRDdae+Xj2BXXMy/c= +go.mau.fi/util v0.8.9-0.20250723171559-474867266038 h1:RVL8TVaYc3LTBBopfjCNDtD+6eZks0O+qgXN/9hsz7k= +go.mau.fi/util v0.8.9-0.20250723171559-474867266038/go.mod h1:GZZp5f9r2MgEu4GDvtB0XxCF7i6Z7Z8fM0w9a5oZH3Y= 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-20250722194234-b61df67bf925 h1:GxOYzZ6x/mRtuIx/ijDy5bGzQkRW8grSET51OfGA1bk= -go.mau.fi/whatsmeow v0.0.0-20250722194234-b61df67bf925/go.mod h1:ltDTXUgOAT7LcFKp11H+5S7UY7+xHBMGzNJcv3dLHGk= +go.mau.fi/whatsmeow v0.0.0-20250723082402-b6b13c69c9cb h1:sMrCRx3Xm1pdbBKm4YFMsjiAmSI23WwKYutR3mGKntE= +go.mau.fi/whatsmeow v0.0.0-20250723082402-b6b13c69c9cb/go.mod h1:ltDTXUgOAT7LcFKp11H+5S7UY7+xHBMGzNJcv3dLHGk= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= -golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc h1:TS73t7x3KarrNd5qAipmspBDS1rkMcgVG/fS1aRb4Rc= -golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc= +golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 h1:R9PFI6EUdfVKgwKjZef7QIwGcBKu86OEFpJ9nUEP2l4= +golang.org/x/exp v0.0.0-20250718183923-645b1fa84792/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc= golang.org/x/image v0.29.0 h1:HcdsyR4Gsuys/Axh0rDEmlBmB68rW1U9BUdB3UVHsas= golang.org/x/image v0.29.0/go.mod h1:RVJROnf3SLK8d26OW91j4FrIHGbsJ8QnbEocVTOWQDA= golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= @@ -101,5 +101,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.24.3-0.20250722203558-463d2ea6d011 h1:s83n+xEOe6iv6K430pwKc4TkId8AJ8DuwvoEIWKEj0o= -maunium.net/go/mautrix v0.24.3-0.20250722203558-463d2ea6d011/go.mod h1:1ut900w++eE9by9yqCR2dQdMqwsHwZG5L+1bKB1EvSA= +maunium.net/go/mautrix v0.24.3-0.20250728125317-74ab3b118e10 h1:IMOGeYKTOzHNnBx1ksVCVCw3t6joQRqDOSI2Ou4Xr/U= +maunium.net/go/mautrix v0.24.3-0.20250728125317-74ab3b118e10/go.mod h1:KrE/TdIeAo6cfAUICmbaiZ18UgHimTOozAGOrioK9SU= From 101906ca3c264a897e9d15936b0c12279974a8b5 Mon Sep 17 00:00:00 2001 From: Kishan Bagaria <1093313+KishanBagaria@users.noreply.github.com> Date: Mon, 28 Jul 2025 20:05:41 +0530 Subject: [PATCH 079/276] client: refactor SendPresence calls (#817) in https://github.com/mautrix/whatsapp/pull/816 we started setting lastPresence in this one, we make sure it's set in some other codepaths --- pkg/connector/client.go | 12 +++++++++--- pkg/connector/handlematrix.go | 2 +- pkg/connector/handlewhatsapp.go | 6 +++--- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/pkg/connector/client.go b/pkg/connector/client.go index 122b393..bfc03cf 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -396,11 +396,9 @@ func (wa *WhatsAppClient) HandleMatrixViewingChat(ctx context.Context, msg *brid } if wa.lastPresence != presence { - err := wa.Client.SendPresence(presence) + err := wa.updatePresence(presence) if err != nil { zerolog.Ctx(ctx).Warn().Err(err).Msg("Failed to set presence when viewing chat") - } else { - wa.lastPresence = presence } } @@ -432,3 +430,11 @@ func (wa *WhatsAppClient) HandleMatrixViewingChat(ctx context.Context, msg *brid return nil } + +func (wa *WhatsAppClient) updatePresence(presence types.Presence) error { + err := wa.Client.SendPresence(presence) + if err == nil { + wa.lastPresence = presence + } + return err +} diff --git a/pkg/connector/handlematrix.go b/pkg/connector/handlematrix.go index a90b677..a97aee7 100644 --- a/pkg/connector/handlematrix.go +++ b/pkg/connector/handlematrix.go @@ -336,7 +336,7 @@ func (wa *WhatsAppClient) HandleMatrixTyping(ctx context.Context, msg *bridgev2. } if wa.Main.Config.SendPresenceOnTyping { - err = wa.Client.SendPresence(types.PresenceAvailable) + err = wa.updatePresence(types.PresenceAvailable) if err != nil { zerolog.Ctx(ctx).Warn().Err(err).Msg("Failed to set presence on typing") } diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index 3386f99..95b6f33 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -128,7 +128,7 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) (success bool) { case *events.AppStateSyncComplete: if len(wa.GetStore().PushName) > 0 && evt.Name == appstate.WAPatchCriticalBlock { - err := wa.Client.SendPresence(types.PresenceUnavailable) + err := wa.updatePresence(types.PresenceUnavailable) if err != nil { log.Warn().Err(err).Msg("Failed to send presence after app state sync") } @@ -141,7 +141,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.Client.SendPresence(types.PresenceUnavailable) + err := wa.updatePresence(types.PresenceUnavailable) if err != nil { log.Warn().Err(err).Msg("Failed to send presence after push name update") } @@ -162,7 +162,7 @@ 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.Client.SendPresence(types.PresenceUnavailable) + err := wa.updatePresence(types.PresenceUnavailable) if err != nil { log.Warn().Err(err).Msg("Failed to send initial presence after connecting") } From 16fa09e94e7c3912262c88b64d98e86bbc7b070b Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 28 Jul 2025 17:35:58 +0300 Subject: [PATCH 080/276] 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 685af2e..c096b2c 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( golang.org/x/sync v0.16.0 google.golang.org/protobuf v1.36.6 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.24.3-0.20250728125317-74ab3b118e10 + maunium.net/go/mautrix v0.24.3-0.20250728143428-ae2c07fb863a ) require ( diff --git a/go.sum b/go.sum index bcdbb97..38c07ea 100644 --- a/go.sum +++ b/go.sum @@ -101,5 +101,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.24.3-0.20250728125317-74ab3b118e10 h1:IMOGeYKTOzHNnBx1ksVCVCw3t6joQRqDOSI2Ou4Xr/U= -maunium.net/go/mautrix v0.24.3-0.20250728125317-74ab3b118e10/go.mod h1:KrE/TdIeAo6cfAUICmbaiZ18UgHimTOozAGOrioK9SU= +maunium.net/go/mautrix v0.24.3-0.20250728143428-ae2c07fb863a h1:3lQKIbNLbHKxMLjwoChuCstvfJ8LT9+So4+QabIjKI8= +maunium.net/go/mautrix v0.24.3-0.20250728143428-ae2c07fb863a/go.mod h1:KrE/TdIeAo6cfAUICmbaiZ18UgHimTOozAGOrioK9SU= From bf96ce9f215a19169fd3b07de530428c666f7181 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 28 Jul 2025 22:04:28 +0300 Subject: [PATCH 081/276] 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 c096b2c..5424852 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( golang.org/x/sync v0.16.0 google.golang.org/protobuf v1.36.6 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.24.3-0.20250728143428-ae2c07fb863a + maunium.net/go/mautrix v0.24.3-0.20250728190343-2e7ff3fedd4c ) require ( diff --git a/go.sum b/go.sum index 38c07ea..fbf371e 100644 --- a/go.sum +++ b/go.sum @@ -101,5 +101,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.24.3-0.20250728143428-ae2c07fb863a h1:3lQKIbNLbHKxMLjwoChuCstvfJ8LT9+So4+QabIjKI8= -maunium.net/go/mautrix v0.24.3-0.20250728143428-ae2c07fb863a/go.mod h1:KrE/TdIeAo6cfAUICmbaiZ18UgHimTOozAGOrioK9SU= +maunium.net/go/mautrix v0.24.3-0.20250728190343-2e7ff3fedd4c h1:ciLyRuYMrqQK8Y0Rp06QcUz5YOnuieNlEbLg65ERLe8= +maunium.net/go/mautrix v0.24.3-0.20250728190343-2e7ff3fedd4c/go.mod h1:KrE/TdIeAo6cfAUICmbaiZ18UgHimTOozAGOrioK9SU= From 261304a4ad5c9d502e58ffd52c4349f122316d76 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 29 Jul 2025 16:18:56 +0300 Subject: [PATCH 082/276] directmedia: add support for avatars --- go.mod | 4 +- go.sum | 8 +- pkg/connector/chatinfo.go | 7 + pkg/connector/directmedia.go | 79 ++++++++++- pkg/connector/userinfo.go | 55 +++++++ pkg/connector/wadb/avatarcache.go | 51 +++++++ pkg/connector/wadb/database.go | 6 + .../wadb/upgrades/00-latest-schema.sql | 12 +- .../upgrades/07-direct-media-avatar-cache.sql | 10 ++ pkg/waid/mediaid.go | 134 ++++++++++++++---- 10 files changed, 323 insertions(+), 43 deletions(-) create mode 100644 pkg/connector/wadb/avatarcache.go create mode 100644 pkg/connector/wadb/upgrades/07-direct-media-avatar-cache.sql diff --git a/go.mod b/go.mod index 5424852..8cc5114 100644 --- a/go.mod +++ b/go.mod @@ -10,13 +10,13 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.8.9-0.20250723171559-474867266038 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250723082402-b6b13c69c9cb + go.mau.fi/whatsmeow v0.0.0-20250729133431-9166d862a88c golang.org/x/image v0.29.0 golang.org/x/net v0.42.0 golang.org/x/sync v0.16.0 google.golang.org/protobuf v1.36.6 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.24.3-0.20250728190343-2e7ff3fedd4c + maunium.net/go/mautrix v0.24.3-0.20250729144204-bcf92ba0e80a ) require ( diff --git a/go.sum b/go.sum index fbf371e..d89d11e 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ go.mau.fi/util v0.8.9-0.20250723171559-474867266038 h1:RVL8TVaYc3LTBBopfjCNDtD+6 go.mau.fi/util v0.8.9-0.20250723171559-474867266038/go.mod h1:GZZp5f9r2MgEu4GDvtB0XxCF7i6Z7Z8fM0w9a5oZH3Y= 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-20250723082402-b6b13c69c9cb h1:sMrCRx3Xm1pdbBKm4YFMsjiAmSI23WwKYutR3mGKntE= -go.mau.fi/whatsmeow v0.0.0-20250723082402-b6b13c69c9cb/go.mod h1:ltDTXUgOAT7LcFKp11H+5S7UY7+xHBMGzNJcv3dLHGk= +go.mau.fi/whatsmeow v0.0.0-20250729133431-9166d862a88c h1:Pms0CGY/5akaS2cl9NwkgZhmoaPAFkeaVUkXDf09Ovc= +go.mau.fi/whatsmeow v0.0.0-20250729133431-9166d862a88c/go.mod h1:ltDTXUgOAT7LcFKp11H+5S7UY7+xHBMGzNJcv3dLHGk= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= @@ -101,5 +101,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.24.3-0.20250728190343-2e7ff3fedd4c h1:ciLyRuYMrqQK8Y0Rp06QcUz5YOnuieNlEbLg65ERLe8= -maunium.net/go/mautrix v0.24.3-0.20250728190343-2e7ff3fedd4c/go.mod h1:KrE/TdIeAo6cfAUICmbaiZ18UgHimTOozAGOrioK9SU= +maunium.net/go/mautrix v0.24.3-0.20250729144204-bcf92ba0e80a h1:ze+5l/XKp38/MgQJCxW5ic/eiZq3YeZ0QPS6M36qmC8= +maunium.net/go/mautrix v0.24.3-0.20250729144204-bcf92ba0e80a/go.mod h1:KrE/TdIeAo6cfAUICmbaiZ18UgHimTOozAGOrioK9SU= diff --git a/pkg/connector/chatinfo.go b/pkg/connector/chatinfo.go index ef52e8d..b83b1f7 100644 --- a/pkg/connector/chatinfo.go +++ b/pkg/connector/chatinfo.go @@ -415,6 +415,12 @@ func (wa *WhatsAppClient) makePortalAvatarFetcher(avatarID string, sender types. return false } else if avatar == nil { return false + } else if wa.Main.MsgConv.DirectMedia { + wrappedAvatar, err = wa.makeDirectMediaAvatar(ctx, jid, avatar, portal.RoomType == database.RoomTypeSpace) + if err != nil { + zerolog.Ctx(ctx).Err(err).Msg("Failed to prepare direct media avatar") + return false + } } else { wrappedAvatar = &bridgev2.Avatar{ ID: networkid.AvatarID(avatar.ID), @@ -454,6 +460,7 @@ func (wa *WhatsAppClient) wrapNewsletterInfo(ctx context.Context, info *types.Ne } } avatar := &bridgev2.Avatar{} + // TODO direct media for newsletter avatars if info.ThreadMeta.Picture != nil { avatar.ID = networkid.AvatarID(info.ThreadMeta.Picture.ID) avatar.Get = func(ctx context.Context) ([]byte, error) { diff --git a/pkg/connector/directmedia.go b/pkg/connector/directmedia.go index 9a5eb86..ec1610c 100644 --- a/pkg/connector/directmedia.go +++ b/pkg/connector/directmedia.go @@ -38,6 +38,7 @@ import ( "maunium.net/go/mautrix/bridgev2/networkid" "maunium.net/go/mautrix/mediaproxy" + "go.mau.fi/mautrix-whatsapp/pkg/connector/wadb" "go.mau.fi/mautrix-whatsapp/pkg/msgconv" "go.mau.fi/mautrix-whatsapp/pkg/waid" ) @@ -55,13 +56,81 @@ var ErrReloadNeeded = mautrix.RespError{ } func (wa *WhatsAppConnector) Download(ctx context.Context, mediaID networkid.MediaID, params map[string]string) (mediaproxy.GetMediaResponse, error) { - parsedID, receiverID, err := waid.ParseMediaID(mediaID) + parsedID, err := waid.ParseMediaID(mediaID) if err != nil { return nil, err } - log := zerolog.Ctx(ctx).With().Any("message_id", parsedID).Logger() + log := zerolog.Ctx(ctx).With().Any("parsed_media_id", parsedID).Logger() ctx = log.WithContext(ctx) - msg, err := wa.Bridge.DB.Message.GetFirstPartByID(ctx, receiverID, parsedID.String()) + if parsedID.Message != nil { + return wa.downloadMessageDirectMedia(ctx, parsedID, params) + } else if parsedID.Avatar != nil { + return wa.downloadAvatarDirectMedia(ctx, parsedID, params) + } else { + return nil, fmt.Errorf("unexpected media ID parsing result") + } +} + +func (wa *WhatsAppConnector) downloadAvatarDirectMedia(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) + } + cachedInfo, err := wa.DB.AvatarCache.Get(ctx, parsedID.Avatar.TargetJID, parsedID.Avatar.AvatarID) + if err != nil { + 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") + } else if cachedInfo == nil || cachedInfo.Expiry.Time.Before(time.Now().Add(5*time.Minute)) { + 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{ + IsCommunity: parsedID.Avatar.Community, + }) + if errors.Is(err, whatsmeow.ErrProfilePictureNotSet) || + errors.Is(err, whatsmeow.ErrProfilePictureUnauthorized) || + (err == nil && (avatar == nil || avatar.ID != parsedID.Avatar.AvatarID)) { + err = wa.DB.AvatarCache.Put(ctx, &wadb.AvatarCacheEntry{ + EntityJID: parsedID.Avatar.TargetJID, + AvatarID: parsedID.Avatar.AvatarID, + Gone: true, + }) + if err != nil { + zerolog.Ctx(ctx).Warn().Err(err). + Str("avatar_id", parsedID.Avatar.AvatarID). + Msg("Failed to mark avatar as gone in cache") + } + 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) + } + cachedInfo = avatarInfoToCacheEntry(ctx, parsedID.Avatar.TargetJID, avatar) + err = wa.DB.AvatarCache.Put(ctx, cachedInfo) + if err != nil { + zerolog.Ctx(ctx).Warn().Err(err). + Str("avatar_id", avatar.ID). + Msg("Failed to update avatar cache entry") + } + } + return &mediaproxy.GetMediaResponseFile{ + Callback: func(w *os.File) error { + return waClient.Client.DownloadMediaWithPathToFile( + ctx, cachedInfo.DirectPath, nil, nil, nil, 0, "", "", w, + ) + }, + ContentType: "", // TODO are avatars always jpeg? + }, nil +} + +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) } else if msg == nil { @@ -77,8 +146,8 @@ func (wa *WhatsAppConnector) Download(ctx context.Context, mediaID networkid.Med return nil, fmt.Errorf("failed to unmarshal media keys: %w", err) } var ul *bridgev2.UserLogin - if receiverID != "" { - ul = wa.Bridge.GetCachedUserLoginByID(receiverID) + if parsedID.UserLogin != "" { + ul = wa.Bridge.GetCachedUserLoginByID(parsedID.UserLogin) } else { logins, err := wa.Bridge.GetUserLoginsInPortal(ctx, msg.Room) if err != nil { diff --git a/pkg/connector/userinfo.go b/pkg/connector/userinfo.go index 0a25d48..320a90a 100644 --- a/pkg/connector/userinfo.go +++ b/pkg/connector/userinfo.go @@ -2,9 +2,12 @@ package connector import ( "context" + "crypto/sha256" "errors" "fmt" "math/rand/v2" + "regexp" + "strconv" "time" "github.com/rs/zerolog" @@ -17,6 +20,7 @@ import ( "maunium.net/go/mautrix/bridgev2/networkid" "maunium.net/go/mautrix/bridgev2/simplevent" + "go.mau.fi/mautrix-whatsapp/pkg/connector/wadb" "go.mau.fi/mautrix-whatsapp/pkg/waid" ) @@ -242,6 +246,51 @@ func updateGhostLastSyncAt(_ context.Context, ghost *bridgev2.Ghost) bool { return forceSave } +var expiryRegex = regexp.MustCompile("oe=([0-9A-Fa-f]+)") + +func avatarInfoToCacheEntry(ctx context.Context, jid types.JID, avatar *types.ProfilePictureInfo) *wadb.AvatarCacheEntry { + expiry := time.Now().Add(24 * time.Hour) + match := expiryRegex.FindStringSubmatch(avatar.DirectPath) + if len(match) == 2 { + expiryUnix, err := strconv.ParseInt(match[1], 16, 64) + if err == nil { + expiry = time.Unix(expiryUnix, 0) + } else { + zerolog.Ctx(ctx).Warn().Err(err). + Strs("match", match). + Msg("Failed to parse expiry from avatar direct path") + } + } + return &wadb.AvatarCacheEntry{ + EntityJID: jid, + AvatarID: avatar.ID, + DirectPath: avatar.DirectPath, + Expiry: jsontime.U(expiry), + Gone: false, + } +} + +func (wa *WhatsAppClient) makeDirectMediaAvatar(ctx context.Context, jid types.JID, avatar *types.ProfilePictureInfo, community bool) (*bridgev2.Avatar, error) { + mxc, err := wa.Main.Bridge.Matrix.GenerateContentURI(ctx, waid.MakeAvatarMediaID(jid, avatar.ID, wa.UserLogin.ID, community)) + if err != nil { + return nil, fmt.Errorf("failed to generate MXC URI: %w", err) + } + cacheEntry := avatarInfoToCacheEntry(ctx, jid, avatar) + err = wa.Main.DB.AvatarCache.Put(ctx, cacheEntry) + if err != nil { + return nil, fmt.Errorf("failed to cache avatar info: %w", err) + } + hash := sha256.Sum256([]byte(avatar.ID)) + if len(avatar.Hash) == 32 { + hash = [32]byte(avatar.Hash) + } + return &bridgev2.Avatar{ + ID: networkid.AvatarID(avatar.ID), + MXC: mxc, + Hash: hash, + }, nil +} + func (wa *WhatsAppClient) fetchGhostAvatar(ctx context.Context, ghost *bridgev2.Ghost) bool { jid := waid.ParseUserID(ghost.ID) existingID := string(ghost.AvatarID) @@ -265,6 +314,12 @@ func (wa *WhatsAppClient) fetchGhostAvatar(ctx context.Context, ghost *bridgev2. return false } else if avatar == nil { return false + } else if wa.Main.MsgConv.DirectMedia { + wrappedAvatar, err = wa.makeDirectMediaAvatar(ctx, jid, avatar, false) + if err != nil { + zerolog.Ctx(ctx).Err(err).Msg("Failed to prepare direct media avatar") + return false + } } else { wrappedAvatar = &bridgev2.Avatar{ ID: networkid.AvatarID(avatar.ID), diff --git a/pkg/connector/wadb/avatarcache.go b/pkg/connector/wadb/avatarcache.go new file mode 100644 index 0000000..8ea947f --- /dev/null +++ b/pkg/connector/wadb/avatarcache.go @@ -0,0 +1,51 @@ +package wadb + +import ( + "context" + + "go.mau.fi/util/dbutil" + "go.mau.fi/util/jsontime" + "go.mau.fi/whatsmeow/types" +) + +type AvatarCacheQuery struct { + *dbutil.QueryHelper[*AvatarCacheEntry] +} + +const ( + getAvatarCacheEntry = ` + SELECT entity_jid, avatar_id, direct_path, expiry, gone + FROM whatsapp_avatar_cache + WHERE entity_jid = $1 AND avatar_id = $2 + ` + putAvatarCacheEntry = ` + INSERT INTO whatsapp_avatar_cache (entity_jid, avatar_id, direct_path, expiry, gone) + VALUES ($1, $2, $3, $4, $5) + ON CONFLICT (entity_jid, avatar_id) DO UPDATE + SET direct_path = EXCLUDED.direct_path, expiry = EXCLUDED.expiry, gone = EXCLUDED.gone + ` +) + +func (acq *AvatarCacheQuery) Get(ctx context.Context, entityJID types.JID, avatarID string) (*AvatarCacheEntry, error) { + return acq.QueryOne(ctx, getAvatarCacheEntry, entityJID, avatarID) +} + +func (acq *AvatarCacheQuery) Put(ctx context.Context, entry *AvatarCacheEntry) error { + return acq.Exec(ctx, putAvatarCacheEntry, entry.sqlVariables()...) +} + +type AvatarCacheEntry struct { + EntityJID types.JID + AvatarID string + DirectPath string + Expiry jsontime.Unix + Gone bool +} + +func (ace *AvatarCacheEntry) Scan(row dbutil.Scannable) (*AvatarCacheEntry, error) { + return dbutil.ValueOrErr(ace, row.Scan(&ace.EntityJID, &ace.AvatarID, &ace.DirectPath, &ace.Expiry, &ace.Gone)) +} + +func (ace *AvatarCacheEntry) sqlVariables() []any { + return []any{ace.EntityJID, ace.AvatarID, ace.DirectPath, ace.Expiry, ace.Gone} +} diff --git a/pkg/connector/wadb/database.go b/pkg/connector/wadb/database.go index 5090980..9d13568 100644 --- a/pkg/connector/wadb/database.go +++ b/pkg/connector/wadb/database.go @@ -15,6 +15,7 @@ type Database struct { PollOption *PollOptionQuery MediaRequest *MediaRequestQuery HSNotif *HistorySyncNotificationQuery + AvatarCache *AvatarCacheQuery } func New(bridgeID networkid.BridgeID, db *dbutil.Database, log zerolog.Logger) *Database { @@ -45,5 +46,10 @@ func New(bridgeID networkid.BridgeID, db *dbutil.Database, log zerolog.Logger) * BridgeID: bridgeID, Database: db, }, + AvatarCache: &AvatarCacheQuery{ + QueryHelper: dbutil.MakeQueryHelper(db, func(_ *dbutil.QueryHelper[*AvatarCacheEntry]) *AvatarCacheEntry { + return &AvatarCacheEntry{} + }), + }, } } diff --git a/pkg/connector/wadb/upgrades/00-latest-schema.sql b/pkg/connector/wadb/upgrades/00-latest-schema.sql index 59baff4..97275f5 100644 --- a/pkg/connector/wadb/upgrades/00-latest-schema.sql +++ b/pkg/connector/wadb/upgrades/00-latest-schema.sql @@ -1,4 +1,4 @@ --- v0 -> v6 (compatible with v3+): Latest revision +-- v0 -> v7 (compatible with v3+): Latest revision CREATE TABLE whatsapp_poll_option_id ( bridge_id TEXT NOT NULL, @@ -88,3 +88,13 @@ CREATE TABLE whatsapp_history_sync_notification ( REFERENCES user_login (bridge_id, id) ON UPDATE CASCADE ON DELETE CASCADE ); CREATE INDEX whatsapp_history_sync_notification_login_idx ON whatsapp_history_sync_notification (bridge_id, user_login_id); + +CREATE TABLE whatsapp_avatar_cache ( + entity_jid TEXT NOT NULL, + avatar_id TEXT NOT NULL, + direct_path TEXT NOT NULL, + expiry BIGINT NOT NULL, + gone BOOLEAN NOT NULL DEFAULT false, + + PRIMARY KEY (entity_jid, avatar_id) +); diff --git a/pkg/connector/wadb/upgrades/07-direct-media-avatar-cache.sql b/pkg/connector/wadb/upgrades/07-direct-media-avatar-cache.sql new file mode 100644 index 0000000..7dd9dba --- /dev/null +++ b/pkg/connector/wadb/upgrades/07-direct-media-avatar-cache.sql @@ -0,0 +1,10 @@ +-- v7 (compatible with v3+): Add cache for avatar URLs when using direct media +CREATE TABLE whatsapp_avatar_cache ( + entity_jid TEXT NOT NULL, + avatar_id TEXT NOT NULL, + direct_path TEXT NOT NULL, + expiry BIGINT NOT NULL, + gone BOOLEAN NOT NULL DEFAULT false, + + PRIMARY KEY (entity_jid, avatar_id) +); diff --git a/pkg/waid/mediaid.go b/pkg/waid/mediaid.go index ab94977..646e23d 100644 --- a/pkg/waid/mediaid.go +++ b/pkg/waid/mediaid.go @@ -17,7 +17,6 @@ package waid import ( - "bytes" "encoding/binary" "encoding/hex" "fmt" @@ -29,12 +28,20 @@ import ( "maunium.net/go/mautrix/bridgev2/networkid" ) +const ( + // Media ID types start from 255, because old media IDs didn't have a type byte and had the length at the start. + mediaIDTypeMessage = 255 + mediaIDTypeAvatar = 254 + mediaIDTypeCommunityAvatar = 253 +) + func MakeMediaID(messageInfo *types.MessageInfo, receiver networkid.UserLoginID) networkid.MediaID { compactChat := compactJID(messageInfo.Chat.ToNonAD()) compactSender := compactJID(messageInfo.Sender.ToNonAD()) receiverID := compactJID(ParseUserLoginID(receiver, 0)) compactID := compactMsgID(messageInfo.ID) - mediaID := make([]byte, 0, 3+len(compactChat)+len(compactSender)+len(compactID)) + mediaID := make([]byte, 0, 5+len(compactChat)+len(compactSender)+len(receiverID)+len(compactID)) + mediaID = append(mediaID, mediaIDTypeMessage) mediaID = append(mediaID, byte(len(compactChat))) mediaID = append(mediaID, compactChat...) mediaID = append(mediaID, byte(len(compactSender))) @@ -46,29 +53,94 @@ func MakeMediaID(messageInfo *types.MessageInfo, receiver networkid.UserLoginID) return mediaID } -func ParseMediaID(mediaID networkid.MediaID) (*ParsedMessageID, networkid.UserLoginID, error) { - reader := bytes.NewReader(mediaID) - chatJID, err := readCompact(reader, parseCompactJID) - if err != nil { - return nil, "", fmt.Errorf("failed to parse chat JID: %w", err) +func MakeAvatarMediaID(targetJID types.JID, id string, receiver networkid.UserLoginID, community bool) networkid.MediaID { + compactTarget := compactJID(targetJID.ToNonAD()) + receiverID := compactJID(ParseUserLoginID(receiver, 0)) + mediaID := make([]byte, 0, 4+len(compactTarget)+len(id)+len(receiverID)) + if community { + mediaID = append(mediaID, mediaIDTypeCommunityAvatar) + } else { + mediaID = append(mediaID, mediaIDTypeAvatar) } - senderJID, err := readCompact(reader, parseCompactJID) - if err != nil { - return nil, "", fmt.Errorf("failed to parse sender JID: %w", err) + mediaID = append(mediaID, byte(len(compactTarget))) + mediaID = append(mediaID, compactTarget...) + mediaID = append(mediaID, byte(len(id))) + mediaID = append(mediaID, id...) + mediaID = append(mediaID, byte(len(receiverID))) + mediaID = append(mediaID, receiverID...) + return mediaID +} + +type AvatarMediaInfo struct { + TargetJID types.JID + AvatarID string + Community bool +} + +type ParsedMediaID struct { + Message *ParsedMessageID + Avatar *AvatarMediaInfo + UserLogin networkid.UserLoginID +} + +func ParseMediaID(mediaID networkid.MediaID) (*ParsedMediaID, error) { + mediaIDType := mediaIDTypeMessage + if mediaID[0] > 127 { + mediaIDType = int(mediaID[0]) + mediaID = mediaID[1:] } - receiverID, err := readCompact(reader, parseCompactJID) - if err != nil { - return nil, "", fmt.Errorf("failed to parse receiver JID: %w", err) + var parsed ParsedMediaID + switch mediaIDType { + case mediaIDTypeMessage: + chatJID, err := readCompact(&mediaID, parseCompactJID) + if err != nil { + return nil, fmt.Errorf("failed to parse chat JID: %w", err) + } + senderJID, err := readCompact(&mediaID, parseCompactJID) + if err != nil { + return nil, fmt.Errorf("failed to parse sender JID: %w", err) + } + receiverID, err := readCompact(&mediaID, parseCompactJID) + if err != nil { + return nil, fmt.Errorf("failed to parse receiver JID: %w", err) + } + id, err := readCompact(&mediaID, parseCompactMsgID) + if err != nil { + return nil, fmt.Errorf("failed to parse message ID: %w", err) + } + parsed.Message = &ParsedMessageID{ + Chat: chatJID, + Sender: senderJID, + ID: id, + } + parsed.UserLogin = MakeUserLoginID(receiverID) + case mediaIDTypeAvatar, mediaIDTypeCommunityAvatar: + targetJID, err := readCompact(&mediaID, parseCompactJID) + if err != nil { + return nil, fmt.Errorf("failed to parse target JID: %w", err) + } + avatarID, err := readCompact(&mediaID, parseString) + if err != nil { + return nil, fmt.Errorf("failed to parse avatar ID: %w", err) + } + receiverID, err := readCompact(&mediaID, parseCompactJID) + if err != nil { + return nil, fmt.Errorf("failed to parse receiver JID: %w", err) + } + parsed.Avatar = &AvatarMediaInfo{ + TargetJID: targetJID, + AvatarID: avatarID, + Community: mediaIDType == mediaIDTypeCommunityAvatar, + } + parsed.UserLogin = MakeUserLoginID(receiverID) + default: + return nil, fmt.Errorf("unknown media ID type %d", mediaIDType) } - id, err := readCompact(reader, parseCompactMsgID) - if err != nil { - return nil, "", fmt.Errorf("failed to parse message ID: %w", err) - } - return &ParsedMessageID{ - Chat: chatJID, - Sender: senderJID, - ID: id, - }, MakeUserLoginID(receiverID), nil + return &parsed, nil +} + +func parseString(data []byte) (string, error) { + return string(data), nil } func isUpperHex(str string) bool { @@ -169,16 +241,16 @@ func parseCompactJID(jid []byte) (types.JID, error) { } } -func readCompact[T any](reader *bytes.Reader, fn func(data []byte) (T, error)) (T, error) { +func readCompact[T any](data *networkid.MediaID, fn func(data []byte) (T, error)) (T, error) { var defVal T - length, err := reader.ReadByte() - if err != nil { - return defVal, err + if len(*data) < 1 { + return defVal, fmt.Errorf("%w (data too short to read length)", io.ErrUnexpectedEOF) } - data := make([]byte, length) - _, err = io.ReadFull(reader, data) - if err != nil { - return defVal, err + length := int((*data)[0]) + if len(*data) < length+1 { + return defVal, fmt.Errorf("%w (wanted %d+1 bytes, only have %d)", io.ErrUnexpectedEOF, length, len(*data)) } - return fn(data) + dataToParse := (*data)[1 : length+1] + *data = (*data)[length+1:] + return fn(dataToParse) } From de08fe910984094ff665243143b872e1749b338a Mon Sep 17 00:00:00 2001 From: Kishan Bagaria <1093313+KishanBagaria@users.noreply.github.com> Date: Tue, 29 Jul 2025 20:22:17 +0530 Subject: [PATCH 083/276] handlewhatsapp: convert HD image/video dual uploads into edits (#818) looks like whatsapp is now uploading a SD version first and replacing the message with a HD version subsequently (which currently shows up as "Unknown message type, please view it on the WhatsApp app") --------- Co-authored-by: Tulir Asokan --- pkg/connector/handlewhatsapp.go | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index 95b6f33..5023be7 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -26,6 +26,7 @@ import ( "github.com/rs/zerolog" "go.mau.fi/util/ptr" "go.mau.fi/whatsmeow/appstate" + "go.mau.fi/whatsmeow/proto/waE2E" "go.mau.fi/whatsmeow/types" "go.mau.fi/whatsmeow/types/events" "maunium.net/go/mautrix/bridgev2" @@ -289,6 +290,27 @@ func (wa *WhatsAppClient) handleWAMessage(ctx context.Context, evt *events.Messa wa.Client.ManualHistorySyncDownload { wa.saveWAHistorySyncNotification(ctx, evt.Message.ProtocolMessage.HistorySyncNotification) } + + messageAssoc := evt.Message.GetMessageContextInfo().GetMessageAssociation() + if assocType := messageAssoc.GetAssociationType(); assocType == waE2E.MessageAssociation_HD_IMAGE_DUAL_UPLOAD || assocType == waE2E.MessageAssociation_HD_VIDEO_DUAL_UPLOAD { + parentKey := messageAssoc.GetParentMessageKey() + associatedMessage := evt.Message.GetAssociatedChildMessage().GetMessage() + wa.UserLogin.Log.Debug(). + Str("message_id", evt.Info.ID). + Str("parent_id", parentKey.GetID()). + Stringer("assoc_type", assocType). + Msg("Received HD replacement message, converting to edit") + + protocolMsg := &waE2E.ProtocolMessage{ + Type: waE2E.ProtocolMessage_MESSAGE_EDIT.Enum(), + Key: parentKey, + EditedMessage: associatedMessage, + } + evt.Message = &waE2E.Message{ + ProtocolMessage: protocolMsg, + } + } + parsedMessageType := getMessageType(evt.Message) if parsedMessageType == "ignore" || strings.HasPrefix(parsedMessageType, "unknown_protocol_") { return From 1dfcdcb463ad621e0ce95cc4dea526bf153cd7bb Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 29 Jul 2025 17:10:25 +0200 Subject: [PATCH 084/276] directmedia: add hacky support for media edits (#819) --- pkg/connector/events.go | 1 + pkg/msgconv/from-whatsapp.go | 6 ++++++ pkg/msgconv/wa-media.go | 3 ++- pkg/waid/mediaid.go | 9 +++++++-- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/pkg/connector/events.go b/pkg/connector/events.go index 76eef7a..6b91630 100644 --- a/pkg/connector/events.go +++ b/pkg/connector/events.go @@ -186,6 +186,7 @@ func (evt *WAMessageEvent) ConvertEdit(ctx context.Context, portal *bridgev2.Por meta.Edits = append(meta.Edits, evt.Info.ID) } + ctx = context.WithValue(ctx, msgconv.ContextKeyEditTargetID, evt.Message.GetProtocolMessage().GetKey().GetID()) cm := evt.wa.Main.MsgConv.ToMatrix(ctx, portal, evt.wa.Client, intent, editedMsg, &evt.Info, evt.isViewOnce(), previouslyConvertedPart) if evt.isUndecryptableUpsertSubEvent && isFailedMedia(cm) { evt.postHandle = func() { diff --git a/pkg/msgconv/from-whatsapp.go b/pkg/msgconv/from-whatsapp.go index 1784ede..5a590ac 100644 --- a/pkg/msgconv/from-whatsapp.go +++ b/pkg/msgconv/from-whatsapp.go @@ -47,6 +47,7 @@ const ( contextKeyClient contextKey = iota contextKeyIntent contextKeyPortal + ContextKeyEditTargetID ) func getClient(ctx context.Context) *whatsmeow.Client { @@ -61,6 +62,11 @@ func getPortal(ctx context.Context) *bridgev2.Portal { return ctx.Value(contextKeyPortal).(*bridgev2.Portal) } +func getEditTargetID(ctx context.Context) types.MessageID { + editID, _ := ctx.Value(ContextKeyEditTargetID).(types.MessageID) + return editID +} + func (mc *MessageConverter) getBasicUserInfo(ctx context.Context, user types.JID) (id.UserID, string, error) { ghost, err := mc.Bridge.GetGhostByID(ctx, waid.MakeUserID(user)) if err != nil { diff --git a/pkg/msgconv/wa-media.go b/pkg/msgconv/wa-media.go index abff34e..d95d8f9 100644 --- a/pkg/msgconv/wa-media.go +++ b/pkg/msgconv/wa-media.go @@ -90,7 +90,8 @@ func (mc *MessageConverter) convertMediaMessage( } var err error portal := getPortal(ctx) - preparedMedia.URL, err = portal.Bridge.Matrix.GenerateContentURI(ctx, waid.MakeMediaID(messageInfo, portal.Receiver)) + idOverride := getEditTargetID(ctx) + preparedMedia.URL, err = portal.Bridge.Matrix.GenerateContentURI(ctx, waid.MakeMediaID(messageInfo, idOverride, portal.Receiver)) if err != nil { panic(fmt.Errorf("failed to generate content URI: %w", err)) } diff --git a/pkg/waid/mediaid.go b/pkg/waid/mediaid.go index 646e23d..6a94de4 100644 --- a/pkg/waid/mediaid.go +++ b/pkg/waid/mediaid.go @@ -35,11 +35,16 @@ const ( mediaIDTypeCommunityAvatar = 253 ) -func MakeMediaID(messageInfo *types.MessageInfo, receiver networkid.UserLoginID) networkid.MediaID { +func MakeMediaID(messageInfo *types.MessageInfo, idOverride types.MessageID, receiver networkid.UserLoginID) networkid.MediaID { compactChat := compactJID(messageInfo.Chat.ToNonAD()) compactSender := compactJID(messageInfo.Sender.ToNonAD()) receiverID := compactJID(ParseUserLoginID(receiver, 0)) - compactID := compactMsgID(messageInfo.ID) + var compactID []byte + if idOverride != "" { + compactID = compactMsgID(idOverride) + } else { + compactID = compactMsgID(messageInfo.ID) + } mediaID := make([]byte, 0, 5+len(compactChat)+len(compactSender)+len(receiverID)+len(compactID)) mediaID = append(mediaID, mediaIDTypeMessage) mediaID = append(mediaID, byte(len(compactChat))) From 8e60111d2eda64b8f25eca8f1c081931a241b7d2 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 29 Jul 2025 19:32:36 +0300 Subject: [PATCH 085/276] userinfo: reduce risk of races when syncing contacts --- pkg/connector/backfill.go | 7 ++++++- pkg/connector/client.go | 2 ++ pkg/connector/commands.go | 4 ++-- pkg/connector/handlewhatsapp.go | 2 +- pkg/connector/userinfo.go | 29 +++++++++++++++++++++++------ 5 files changed, 34 insertions(+), 10 deletions(-) diff --git a/pkg/connector/backfill.go b/pkg/connector/backfill.go index 78a5a4f..96c51c3 100644 --- a/pkg/connector/backfill.go +++ b/pkg/connector/backfill.go @@ -157,7 +157,12 @@ func (wa *WhatsAppClient) handleWAHistorySync(ctx context.Context, evt *waHistor if evt.GetGlobalSettings() != nil { log.Debug().Interface("global_settings", evt.GetGlobalSettings()).Msg("Got global settings in history sync") } - if evt.GetSyncType() == waHistorySync.HistorySync_INITIAL_STATUS_V3 || evt.GetSyncType() == waHistorySync.HistorySync_PUSH_NAME || evt.GetSyncType() == waHistorySync.HistorySync_NON_BLOCKING_DATA { + if evt.GetSyncType() == waHistorySync.HistorySync_INITIAL_STATUS_V3 || + evt.GetSyncType() == waHistorySync.HistorySync_PUSH_NAME || + evt.GetSyncType() == waHistorySync.HistorySync_NON_BLOCKING_DATA { + if evt.GetSyncType() == waHistorySync.HistorySync_PUSH_NAME { + wa.pushNamesSynced.Set() + } log.Debug(). Int("conversation_count", len(evt.GetConversations())). Int("pushname_count", len(evt.GetPushnames())). diff --git a/pkg/connector/client.go b/pkg/connector/client.go index bfc03cf..0750526 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -25,6 +25,7 @@ import ( "time" "github.com/rs/zerolog" + "go.mau.fi/util/exsync" "go.mau.fi/whatsmeow" waBinary "go.mau.fi/whatsmeow/binary" "go.mau.fi/whatsmeow/proto/waHistorySync" @@ -112,6 +113,7 @@ type WhatsAppClient struct { mediaRetryLock *semaphore.Weighted offlineSyncWaiter chan error isNewLogin bool + pushNamesSynced exsync.Event lastPresence types.Presence } diff --git a/pkg/connector/commands.go b/pkg/connector/commands.go index 3017b15..8868741 100644 --- a/pkg/connector/commands.go +++ b/pkg/connector/commands.go @@ -135,10 +135,10 @@ func fnSync(ce *commands.Event) { } ce.Reply("Queued syncs for %d groups", len(groups)) case "contacts": - wa.resyncContacts(false) + wa.resyncContacts(false, false) ce.React("✅") case "contacts-with-avatars": - wa.resyncContacts(true) + wa.resyncContacts(true, false) ce.React("✅") case "appstate": for _, name := range appstate.AllPatchNames { diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index 5023be7..2207540 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -135,7 +135,7 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) (success bool) { } go wa.syncRemoteProfile(log.WithContext(context.Background()), nil) } else if evt.Name == appstate.WAPatchCriticalUnblockLow { - go wa.resyncContacts(false) + go wa.resyncContacts(false, true) } case *events.AppState: // Intentionally ignored diff --git a/pkg/connector/userinfo.go b/pkg/connector/userinfo.go index 320a90a..42763cf 100644 --- a/pkg/connector/userinfo.go +++ b/pkg/connector/userinfo.go @@ -331,20 +331,37 @@ func (wa *WhatsAppClient) fetchGhostAvatar(ctx context.Context, ghost *bridgev2. return ghost.UpdateAvatar(ctx, wrappedAvatar) } -func (wa *WhatsAppClient) resyncContacts(forceAvatarSync bool) { +func (wa *WhatsAppClient) resyncContacts(forceAvatarSync, automatic bool) { log := wa.UserLogin.Log.With().Str("action", "resync contacts").Logger() - ctx := log.WithContext(context.Background()) - contacts, err := wa.GetStore().Contacts.GetAllContacts(ctx) + ctx := log.WithContext(wa.Main.Bridge.BackgroundCtx) + if automatic && wa.isNewLogin { + log.Debug().Msg("Waiting for push name history sync before resyncing contacts") + timeoutCtx, cancel := context.WithTimeout(ctx, 5*time.Minute) + _ = wa.pushNamesSynced.Wait(timeoutCtx) + cancel() + if ctx.Err() != nil { + return + } + } + contactStore := wa.GetStore().Contacts + contacts, err := contactStore.GetAllContacts(ctx) if err != nil { log.Err(err).Msg("Failed to get cached contacts") return } log.Info().Int("contact_count", len(contacts)).Msg("Resyncing displaynames with contact info") - for jid, contact := range contacts { + for jid := range contacts { + if ctx.Err() != nil { + return + } ghost, err := wa.Main.Bridge.GetGhostByID(ctx, waid.MakeUserID(jid)) if err != nil { - log.Err(err).Msg("Failed to get ghost") - } else if ghost != nil { + log.Err(err).Stringer("jid", jid).Msg("Failed to get ghost") + // Refetch contact info from the store to reduce the risk of races. + // This should always hit the cache. + } else if contact, err := contactStore.GetContact(ctx, jid); err != nil { + log.Err(err).Stringer("jid", jid).Msg("Failed to get contact info") + } else { userInfo := wa.contactToUserInfo(ctx, jid, contact, forceAvatarSync || ghost.AvatarID == "") ghost.UpdateInfo(ctx, userInfo) wa.syncAltGhostWithInfo(ctx, jid, userInfo) From 2624702a39a428da9d7d7fff2fdf86325a4a120e Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 30 Jul 2025 13:27:16 +0300 Subject: [PATCH 086/276] client: fix potential deadlock in background connections --- go.mod | 2 +- go.sum | 4 ++-- pkg/connector/client.go | 18 +++++++++++++----- pkg/connector/handlewhatsapp.go | 5 ++++- 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 8cc5114..329e187 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.8.9-0.20250723171559-474867266038 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250729133431-9166d862a88c + go.mau.fi/whatsmeow v0.0.0-20250730102611-88501e9b1125 golang.org/x/image v0.29.0 golang.org/x/net v0.42.0 golang.org/x/sync v0.16.0 diff --git a/go.sum b/go.sum index d89d11e..d642bde 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ go.mau.fi/util v0.8.9-0.20250723171559-474867266038 h1:RVL8TVaYc3LTBBopfjCNDtD+6 go.mau.fi/util v0.8.9-0.20250723171559-474867266038/go.mod h1:GZZp5f9r2MgEu4GDvtB0XxCF7i6Z7Z8fM0w9a5oZH3Y= 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-20250729133431-9166d862a88c h1:Pms0CGY/5akaS2cl9NwkgZhmoaPAFkeaVUkXDf09Ovc= -go.mau.fi/whatsmeow v0.0.0-20250729133431-9166d862a88c/go.mod h1:ltDTXUgOAT7LcFKp11H+5S7UY7+xHBMGzNJcv3dLHGk= +go.mau.fi/whatsmeow v0.0.0-20250730102611-88501e9b1125 h1:KkmlnwgYizUr+E5V+hjFRuwouKnXpHizF/dz00qeVr0= +go.mau.fi/whatsmeow v0.0.0-20250730102611-88501e9b1125/go.mod h1:ltDTXUgOAT7LcFKp11H+5S7UY7+xHBMGzNJcv3dLHGk= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= diff --git a/pkg/connector/client.go b/pkg/connector/client.go index 0750526..f4dd100 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -111,7 +111,7 @@ type WhatsAppClient struct { directMediaRetries map[networkid.MessageID]*directMediaRetry directMediaLock sync.Mutex mediaRetryLock *semaphore.Weighted - offlineSyncWaiter chan error + offlineSyncWaiter atomic.Pointer[chan error] isNewLogin bool pushNamesSynced exsync.Event lastPresence types.Presence @@ -209,8 +209,14 @@ func (wa *WhatsAppClient) Connect(ctx context.Context) { } func (wa *WhatsAppClient) notifyOfflineSyncWaiter(err error) { - if wa.offlineSyncWaiter != nil { - wa.offlineSyncWaiter <- err + if ch := wa.offlineSyncWaiter.Load(); ch != nil { + select { + case *ch <- err: + default: + wa.UserLogin.Log.Warn(). + AnErr("dropped_error", err). + Msg("Offline sync waiter channel was full, dropping input") + } } } @@ -232,7 +238,9 @@ func (wa *WhatsAppClient) ConnectBackground(ctx context.Context, params *bridgev return bridgev2.ErrNotLoggedIn } wa.Client.BackgroundEventCtx = wa.UserLogin.Log.WithContext(wa.Main.Bridge.BackgroundCtx) - wa.offlineSyncWaiter = make(chan error) + ch := make(chan error, 1) + wa.offlineSyncWaiter.Store(&ch) + defer wa.offlineSyncWaiter.Store(nil) wa.Main.backgroundConnectOnce.Do(wa.Main.onFirstBackgroundConnect) if err := wa.Main.updateProxy(ctx, wa.Client, false); err != nil { zerolog.Ctx(ctx).Err(err).Msg("Failed to update proxy") @@ -253,7 +261,7 @@ func (wa *WhatsAppClient) ConnectBackground(ctx context.Context, params *bridgev select { case <-ctx.Done(): return ctx.Err() - case err = <-wa.offlineSyncWaiter: + case err = <-ch: if err == nil { var data wrappedPushNotificationData err = json.Unmarshal(params.RawData, &data) diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index 2207540..e8a0d5a 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -180,10 +180,13 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) (success bool) { case *events.OfflineSyncCompleted: if !wa.PhoneRecentlySeen(true) { log.Info(). + Int("evt_count", evt.Count). Time("phone_last_seen", wa.UserLogin.Metadata.(*waid.UserLoginMetadata).PhoneLastSeen.Time). Msg("Offline sync completed, but phone last seen date is still old") } else { - log.Info().Msg("Offline sync completed") + log.Info(). + Int("evt_count", evt.Count). + Msg("Offline sync completed") } wa.UserLogin.BridgeState.Send(status.BridgeState{StateEvent: status.StateConnected}) wa.notifyOfflineSyncWaiter(nil) From c21648f31e7bb84521a35d73b1ce10ffc77f3a2b Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 31 Jul 2025 12:38:47 +0300 Subject: [PATCH 087/276] backfill: unwrap associated child messages --- pkg/connector/backfill.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkg/connector/backfill.go b/pkg/connector/backfill.go index 96c51c3..34322ce 100644 --- a/pkg/connector/backfill.go +++ b/pkg/connector/backfill.go @@ -518,6 +518,11 @@ func (wa *WhatsAppClient) FetchMessages(ctx context.Context, params bridgev2.Fet func (wa *WhatsAppClient) convertHistorySyncMessage( ctx context.Context, portal *bridgev2.Portal, info *types.MessageInfo, msg *waE2E.Message, isViewOnce bool, reactions []*waWeb.Reaction, ) (*bridgev2.BackfillMessage, *wadb.MediaRequest) { + // New messages turn these into edits, but in backfill we only have the last version, + // so no need to do the edit thing. Instead, just unwrap the message. + if msg.GetAssociatedChildMessage().GetMessage() != nil { + msg = msg.GetAssociatedChildMessage().GetMessage() + } // TODO use proper intent intent := wa.Main.Bridge.Bot wrapped := &bridgev2.BackfillMessage{ From 81875864edafa3a5ba79a13917fa4459652a2363 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 31 Jul 2025 14:28:07 +0300 Subject: [PATCH 088/276] 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 329e187..50c417c 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( golang.org/x/sync v0.16.0 google.golang.org/protobuf v1.36.6 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.24.3-0.20250729144204-bcf92ba0e80a + maunium.net/go/mautrix v0.24.3-0.20250731110000-94f53c5853c6 ) require ( diff --git a/go.sum b/go.sum index d642bde..7b921bf 100644 --- a/go.sum +++ b/go.sum @@ -101,5 +101,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.24.3-0.20250729144204-bcf92ba0e80a h1:ze+5l/XKp38/MgQJCxW5ic/eiZq3YeZ0QPS6M36qmC8= -maunium.net/go/mautrix v0.24.3-0.20250729144204-bcf92ba0e80a/go.mod h1:KrE/TdIeAo6cfAUICmbaiZ18UgHimTOozAGOrioK9SU= +maunium.net/go/mautrix v0.24.3-0.20250731110000-94f53c5853c6 h1:27wTzsIxqJ+xzqcGGSXfe3MGXHPgzmWHO2+lw7V8Tdk= +maunium.net/go/mautrix v0.24.3-0.20250731110000-94f53c5853c6/go.mod h1:KrE/TdIeAo6cfAUICmbaiZ18UgHimTOozAGOrioK9SU= From 262e7fc79311a73eca6cdbc379d3f62aafdbc4f3 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 31 Jul 2025 14:54:13 +0300 Subject: [PATCH 089/276] Revert "msgconv/from-matrix: try to escape formatting characters in plain text" This reverts commit d61c197be50dda58e6b88bdae2913ba79d1a934e. --- pkg/msgconv/from-matrix.go | 9 +-------- pkg/msgconv/msgconv.go | 3 --- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/pkg/msgconv/from-matrix.go b/pkg/msgconv/from-matrix.go index edf33f1..105c1c2 100644 --- a/pkg/msgconv/from-matrix.go +++ b/pkg/msgconv/from-matrix.go @@ -279,13 +279,6 @@ func (mc *MessageConverter) constructMediaMessage( } } -var hackyFormattingEscaper = strings.NewReplacer( - "~", "\u200b~", - "_", "\u200b_", - "*", "\u200b*", - "`", "\u200b`", -) - func (mc *MessageConverter) parseText(ctx context.Context, content *event.MessageEventContent) (text string, mentions []string) { mentions = make([]string, 0) @@ -295,7 +288,7 @@ func (mc *MessageConverter) parseText(ctx context.Context, content *event.Messag if content.Format == event.FormatHTML { text = mc.HTMLParser.Parse(content.FormattedBody, parseCtx) } else { - text = hackyFormattingEscaper.Replace(content.Body) + text = content.Body } return } diff --git a/pkg/msgconv/msgconv.go b/pkg/msgconv/msgconv.go index daa00b5..e4109c8 100644 --- a/pkg/msgconv/msgconv.go +++ b/pkg/msgconv/msgconv.go @@ -69,9 +69,6 @@ func New(br *bridgev2.Bridge) *MessageConverter { MonospaceBlockConverter: func(code, language string, ctx format.Context) string { return "```\n" + code + "\n```" }, - TextConverter: func(s string, context format.Context) string { - return hackyFormattingEscaper.Replace(s) - }, } return mc } From 56c157ad5e952e3cfcb681a71b783cdacd74f1d3 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 31 Jul 2025 17:20:56 +0300 Subject: [PATCH 090/276] client: fix push name syncing flag --- pkg/connector/client.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/connector/client.go b/pkg/connector/client.go index f4dd100..a0951f8 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -52,6 +52,7 @@ func (wa *WhatsAppConnector) LoadUserLogin(ctx context.Context, login *bridgev2. resyncQueue: make(map[types.JID]resyncQueueItem), directMediaRetries: make(map[networkid.MessageID]*directMediaRetry), mediaRetryLock: semaphore.NewWeighted(wa.Config.HistorySync.MediaRequests.MaxAsyncHandle), + pushNamesSynced: exsync.NewEvent(), } login.Client = w @@ -113,7 +114,7 @@ type WhatsAppClient struct { mediaRetryLock *semaphore.Weighted offlineSyncWaiter atomic.Pointer[chan error] isNewLogin bool - pushNamesSynced exsync.Event + pushNamesSynced *exsync.Event lastPresence types.Presence } From 394622e20545b5f978e432663f6733a92ff63321 Mon Sep 17 00:00:00 2001 From: Brad Murray Date: Thu, 31 Jul 2025 13:07:22 -0400 Subject: [PATCH 091/276] login: add better error codes for invalid login inputs (#821) --- go.mod | 2 +- go.sum | 4 ++-- pkg/connector/login.go | 16 ++++++++++++++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 50c417c..f423f49 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.8.9-0.20250723171559-474867266038 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250730102611-88501e9b1125 + go.mau.fi/whatsmeow v0.0.0-20250731124915-c8a3f7009971 golang.org/x/image v0.29.0 golang.org/x/net v0.42.0 golang.org/x/sync v0.16.0 diff --git a/go.sum b/go.sum index 7b921bf..c0eeac9 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ go.mau.fi/util v0.8.9-0.20250723171559-474867266038 h1:RVL8TVaYc3LTBBopfjCNDtD+6 go.mau.fi/util v0.8.9-0.20250723171559-474867266038/go.mod h1:GZZp5f9r2MgEu4GDvtB0XxCF7i6Z7Z8fM0w9a5oZH3Y= 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-20250730102611-88501e9b1125 h1:KkmlnwgYizUr+E5V+hjFRuwouKnXpHizF/dz00qeVr0= -go.mau.fi/whatsmeow v0.0.0-20250730102611-88501e9b1125/go.mod h1:ltDTXUgOAT7LcFKp11H+5S7UY7+xHBMGzNJcv3dLHGk= +go.mau.fi/whatsmeow v0.0.0-20250731124915-c8a3f7009971 h1:7rtwNU41DumwGNAKlPHgbyLau2siuFhQDsJ3OO8uN3g= +go.mau.fi/whatsmeow v0.0.0-20250731124915-c8a3f7009971/go.mod h1:ltDTXUgOAT7LcFKp11H+5S7UY7+xHBMGzNJcv3dLHGk= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= diff --git a/pkg/connector/login.go b/pkg/connector/login.go index ebf3060..2442356 100644 --- a/pkg/connector/login.go +++ b/pkg/connector/login.go @@ -2,6 +2,7 @@ package connector import ( "context" + "errors" "fmt" "net/http" "sync/atomic" @@ -63,6 +64,16 @@ var ( Err: "Unexpected event while waiting for login", StatusCode: http.StatusInternalServerError, } + ErrPhoneNumberTooShort = bridgev2.RespError{ + ErrCode: "FI.MAU.WHATSAPP.PHONE_NUMBER_TOO_SHORT", + Err: "Phone number too short", + StatusCode: http.StatusBadRequest, + } + ErrPhoneNumberIsNotInternational = bridgev2.RespError{ + ErrCode: "FI.MAU.WHATSAPP.PHONE_NUMBER_NOT_INTERNATIONAL", + Err: "Phone number must be in international format", + StatusCode: http.StatusBadRequest, + } ) func (wa *WhatsAppConnector) CreateLogin(_ context.Context, user *bridgev2.User, flowID string) (bridgev2.LoginProcess, error) { @@ -181,6 +192,11 @@ func (wl *WALogin) SubmitUserInput(ctx context.Context, input map[string]string) pairingCode, err := wl.Client.PairPhone(ctx, input["phone_number"], true, whatsmeow.PairClientChrome, "Chrome (Linux)") if err != nil { wl.Log.Err(err).Msg("Failed to request phone code login") + if errors.Is(err, whatsmeow.ErrPhoneNumberTooShort) { + return nil, ErrPhoneNumberTooShort + } else if errors.Is(err, whatsmeow.ErrPhoneNumberIsNotInternational) { + return nil, ErrPhoneNumberIsNotInternational + } return nil, err } wl.Log.Debug().Msg("Phone code login started") From 40fe582cf91498ec74aa107654c3d0c0717fec96 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 1 Aug 2025 11:37:34 +0300 Subject: [PATCH 092/276] 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 f423f49..c0f0db2 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( golang.org/x/sync v0.16.0 google.golang.org/protobuf v1.36.6 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.24.3-0.20250731110000-94f53c5853c6 + maunium.net/go/mautrix v0.24.3-0.20250801084753-196164ed6749 ) require ( diff --git a/go.sum b/go.sum index c0eeac9..96dd94c 100644 --- a/go.sum +++ b/go.sum @@ -101,5 +101,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.24.3-0.20250731110000-94f53c5853c6 h1:27wTzsIxqJ+xzqcGGSXfe3MGXHPgzmWHO2+lw7V8Tdk= -maunium.net/go/mautrix v0.24.3-0.20250731110000-94f53c5853c6/go.mod h1:KrE/TdIeAo6cfAUICmbaiZ18UgHimTOozAGOrioK9SU= +maunium.net/go/mautrix v0.24.3-0.20250801084753-196164ed6749 h1:eEIsN9CufSHs4SeRjUwIpJo3n8QaX0rfO2sEERjPkKY= +maunium.net/go/mautrix v0.24.3-0.20250801084753-196164ed6749/go.mod h1:KrE/TdIeAo6cfAUICmbaiZ18UgHimTOozAGOrioK9SU= From 3cefbb2cfc41656a535f4905d69ad07dd557bd55 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 3 Aug 2025 19:51:53 +0300 Subject: [PATCH 093/276] msgconv/from-matrix: restore passthrough of animated webp stickers --- pkg/msgconv/from-matrix.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/pkg/msgconv/from-matrix.go b/pkg/msgconv/from-matrix.go index 105c1c2..a71824a 100644 --- a/pkg/msgconv/from-matrix.go +++ b/pkg/msgconv/from-matrix.go @@ -440,11 +440,16 @@ func (mc *MessageConverter) reuploadFileToWhatsApp( var size int data, size, err = mc.convertToWebP(data) if err != nil { - return nil, nil, "image/webp", fmt.Errorf("%w (to webp): %w", bridgev2.ErrMediaConvertFailed, err) + if mime != "image/webp" { + return nil, nil, "image/webp", fmt.Errorf("%w (to webp): %w", bridgev2.ErrMediaConvertFailed, err) + } else { + zerolog.Ctx(ctx).Warn().Err(err).Msg("Failed to add padding to webp, continuing with original file") + } + } else { + content.Info.Width = size + content.Info.Height = size + mime = "image/webp" } - content.Info.Width = size - content.Info.Height = size - mime = "image/webp" } case event.MsgImage: mediaType = whatsmeow.MediaImage From 18684ac5b272f46b4f23651854b4834c483c3c79 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 5 Aug 2025 12:48:41 +0300 Subject: [PATCH 094/276] userinfo: update whatsmeow to fix new group member names in certain cases --- go.mod | 2 +- go.sum | 4 ++-- pkg/connector/userinfo.go | 4 +++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index c0f0db2..91fb60e 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.8.9-0.20250723171559-474867266038 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250731124915-c8a3f7009971 + go.mau.fi/whatsmeow v0.0.0-20250805094724-a2272061b926 golang.org/x/image v0.29.0 golang.org/x/net v0.42.0 golang.org/x/sync v0.16.0 diff --git a/go.sum b/go.sum index 96dd94c..6dd6c58 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ go.mau.fi/util v0.8.9-0.20250723171559-474867266038 h1:RVL8TVaYc3LTBBopfjCNDtD+6 go.mau.fi/util v0.8.9-0.20250723171559-474867266038/go.mod h1:GZZp5f9r2MgEu4GDvtB0XxCF7i6Z7Z8fM0w9a5oZH3Y= 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-20250731124915-c8a3f7009971 h1:7rtwNU41DumwGNAKlPHgbyLau2siuFhQDsJ3OO8uN3g= -go.mau.fi/whatsmeow v0.0.0-20250731124915-c8a3f7009971/go.mod h1:ltDTXUgOAT7LcFKp11H+5S7UY7+xHBMGzNJcv3dLHGk= +go.mau.fi/whatsmeow v0.0.0-20250805094724-a2272061b926 h1:ZEY7vEnglugWtLwmRDsmVsyThdrdLhcNZAhTbAmGuzs= +go.mau.fi/whatsmeow v0.0.0-20250805094724-a2272061b926/go.mod h1:ltDTXUgOAT7LcFKp11H+5S7UY7+xHBMGzNJcv3dLHGk= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= diff --git a/pkg/connector/userinfo.go b/pkg/connector/userinfo.go index 42763cf..bfa9865 100644 --- a/pkg/connector/userinfo.go +++ b/pkg/connector/userinfo.go @@ -199,7 +199,9 @@ func (wa *WhatsAppClient) contactToUserInfo(ctx context.Context, jid types.JID, pnJID, err := wa.GetStore().LIDs.GetPNForLID(ctx, jid) if err != nil { zerolog.Ctx(ctx).Err(err).Stringer("lid", jid).Msg("Failed to get PN for LID") - } else if !pnJID.IsEmpty() { + } else if pnJID.IsEmpty() { + zerolog.Ctx(ctx).Debug().Stringer("lid", jid).Msg("Phone number not found for LID in contactToUserInfo") + } else { phone = "+" + pnJID.User extraContact, err := wa.GetStore().Contacts.GetContact(ctx, pnJID) if err != nil { From 38ccb929011084421e909b577b081befa02708a2 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 6 Aug 2025 16:37:23 +0300 Subject: [PATCH 095/276] msgconv/from-whatsapp: include raw data in unknown message info --- go.mod | 2 +- go.sum | 4 ++-- pkg/connector/backfill.go | 8 +++++--- pkg/connector/events.go | 8 ++++++-- pkg/msgconv/from-whatsapp.go | 3 ++- 5 files changed, 16 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 91fb60e..9c30a0c 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.8.9-0.20250723171559-474867266038 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250805094724-a2272061b926 + go.mau.fi/whatsmeow v0.0.0-20250806133617-13d563e58486 golang.org/x/image v0.29.0 golang.org/x/net v0.42.0 golang.org/x/sync v0.16.0 diff --git a/go.sum b/go.sum index 6dd6c58..ea959ca 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ go.mau.fi/util v0.8.9-0.20250723171559-474867266038 h1:RVL8TVaYc3LTBBopfjCNDtD+6 go.mau.fi/util v0.8.9-0.20250723171559-474867266038/go.mod h1:GZZp5f9r2MgEu4GDvtB0XxCF7i6Z7Z8fM0w9a5oZH3Y= 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-20250805094724-a2272061b926 h1:ZEY7vEnglugWtLwmRDsmVsyThdrdLhcNZAhTbAmGuzs= -go.mau.fi/whatsmeow v0.0.0-20250805094724-a2272061b926/go.mod h1:ltDTXUgOAT7LcFKp11H+5S7UY7+xHBMGzNJcv3dLHGk= +go.mau.fi/whatsmeow v0.0.0-20250806133617-13d563e58486 h1:JvjaCc5uM5sdzRx0keOB88Tjv2mJu+i5rLVW7vK0iyg= +go.mau.fi/whatsmeow v0.0.0-20250806133617-13d563e58486/go.mod h1:ltDTXUgOAT7LcFKp11H+5S7UY7+xHBMGzNJcv3dLHGk= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= diff --git a/pkg/connector/backfill.go b/pkg/connector/backfill.go index 34322ce..dce0038 100644 --- a/pkg/connector/backfill.go +++ b/pkg/connector/backfill.go @@ -471,7 +471,9 @@ func (wa *WhatsAppClient) FetchMessages(ctx context.Context, params bridgev2.Fet } var mediaReq *wadb.MediaRequest isViewOnce := evt.IsViewOnce || evt.IsViewOnceV2 || evt.IsViewOnceV2Extension - convertedMessages[i], mediaReq = wa.convertHistorySyncMessage(ctx, params.Portal, &evt.Info, evt.Message, isViewOnce, msg.Reactions) + convertedMessages[i], mediaReq = wa.convertHistorySyncMessage( + ctx, params.Portal, &evt.Info, evt.Message, evt.RawMessage, isViewOnce, msg.Reactions, + ) if mediaReq != nil { mediaRequests = append(mediaRequests, mediaReq) } @@ -516,7 +518,7 @@ func (wa *WhatsAppClient) FetchMessages(ctx context.Context, params bridgev2.Fet } func (wa *WhatsAppClient) convertHistorySyncMessage( - ctx context.Context, portal *bridgev2.Portal, info *types.MessageInfo, msg *waE2E.Message, isViewOnce bool, reactions []*waWeb.Reaction, + ctx context.Context, portal *bridgev2.Portal, info *types.MessageInfo, msg, rawMsg *waE2E.Message, isViewOnce bool, reactions []*waWeb.Reaction, ) (*bridgev2.BackfillMessage, *wadb.MediaRequest) { // New messages turn these into edits, but in backfill we only have the last version, // so no need to do the edit thing. Instead, just unwrap the message. @@ -526,7 +528,7 @@ func (wa *WhatsAppClient) convertHistorySyncMessage( // TODO use proper intent intent := wa.Main.Bridge.Bot wrapped := &bridgev2.BackfillMessage{ - ConvertedMessage: wa.Main.MsgConv.ToMatrix(ctx, portal, wa.Client, intent, msg, info, isViewOnce, nil), + ConvertedMessage: wa.Main.MsgConv.ToMatrix(ctx, portal, wa.Client, intent, msg, rawMsg, info, isViewOnce, nil), Sender: wa.makeEventSender(ctx, info.Sender), ID: waid.MakeMessageID(info.Chat, info.Sender, info.ID), TxnID: networkid.TransactionID(waid.MakeMessageID(info.Chat, info.Sender, info.ID)), diff --git a/pkg/connector/events.go b/pkg/connector/events.go index 6b91630..2917f91 100644 --- a/pkg/connector/events.go +++ b/pkg/connector/events.go @@ -187,7 +187,9 @@ func (evt *WAMessageEvent) ConvertEdit(ctx context.Context, portal *bridgev2.Por } ctx = context.WithValue(ctx, msgconv.ContextKeyEditTargetID, evt.Message.GetProtocolMessage().GetKey().GetID()) - cm := evt.wa.Main.MsgConv.ToMatrix(ctx, portal, evt.wa.Client, intent, editedMsg, &evt.Info, evt.isViewOnce(), previouslyConvertedPart) + cm := evt.wa.Main.MsgConv.ToMatrix( + ctx, portal, evt.wa.Client, intent, editedMsg, evt.MsgEvent.RawMessage, &evt.Info, evt.isViewOnce(), previouslyConvertedPart, + ) if evt.isUndecryptableUpsertSubEvent && isFailedMedia(cm) { evt.postHandle = func() { evt.wa.processFailedMedia(ctx, portal.PortalKey, evt.GetID(), cm, false) @@ -262,7 +264,9 @@ func (evt *WAMessageEvent) HandleExisting(ctx context.Context, portal *bridgev2. func (evt *WAMessageEvent) ConvertMessage(ctx context.Context, portal *bridgev2.Portal, intent bridgev2.MatrixAPI) (*bridgev2.ConvertedMessage, error) { evt.wa.EnqueuePortalResync(portal) - converted := evt.wa.Main.MsgConv.ToMatrix(ctx, portal, evt.wa.Client, intent, evt.Message, &evt.Info, evt.isViewOnce(), nil) + converted := evt.wa.Main.MsgConv.ToMatrix( + ctx, portal, evt.wa.Client, intent, evt.Message, evt.MsgEvent.RawMessage, &evt.Info, evt.isViewOnce(), nil, + ) if isFailedMedia(converted) { evt.postHandle = func() { evt.wa.processFailedMedia(ctx, portal.PortalKey, evt.GetID(), converted, false) diff --git a/pkg/msgconv/from-whatsapp.go b/pkg/msgconv/from-whatsapp.go index 5a590ac..401a19d 100644 --- a/pkg/msgconv/from-whatsapp.go +++ b/pkg/msgconv/from-whatsapp.go @@ -134,6 +134,7 @@ func (mc *MessageConverter) ToMatrix( client *whatsmeow.Client, intent bridgev2.MatrixAPI, waMsg *waE2E.Message, + rawWaMsg *waE2E.Message, info *types.MessageInfo, isViewOnce bool, previouslyConvertedPart *bridgev2.ConvertedMessagePart, @@ -209,7 +210,7 @@ func (mc *MessageConverter) ToMatrix( case waMsg.EncCommentMessage != nil: part = failedCommentPart default: - part, contextInfo = mc.convertUnknownMessage(ctx, waMsg) + part, contextInfo = mc.convertUnknownMessage(ctx, rawWaMsg) } part.Content.Mentions = &event.Mentions{} From 1b65e1aa45741727fba9546060b4acaeff94f955 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 7 Aug 2025 10:22:08 +0300 Subject: [PATCH 096/276] 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 9c30a0c..b5c59f2 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.8.9-0.20250723171559-474867266038 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250806133617-13d563e58486 + go.mau.fi/whatsmeow v0.0.0-20250807072145-72ce90b82194 golang.org/x/image v0.29.0 golang.org/x/net v0.42.0 golang.org/x/sync v0.16.0 diff --git a/go.sum b/go.sum index ea959ca..adb9935 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ go.mau.fi/util v0.8.9-0.20250723171559-474867266038 h1:RVL8TVaYc3LTBBopfjCNDtD+6 go.mau.fi/util v0.8.9-0.20250723171559-474867266038/go.mod h1:GZZp5f9r2MgEu4GDvtB0XxCF7i6Z7Z8fM0w9a5oZH3Y= 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-20250806133617-13d563e58486 h1:JvjaCc5uM5sdzRx0keOB88Tjv2mJu+i5rLVW7vK0iyg= -go.mau.fi/whatsmeow v0.0.0-20250806133617-13d563e58486/go.mod h1:ltDTXUgOAT7LcFKp11H+5S7UY7+xHBMGzNJcv3dLHGk= +go.mau.fi/whatsmeow v0.0.0-20250807072145-72ce90b82194 h1:/ow/oKzvxxwJEyCJ4bq8U7W2yar2f0HSq89yto+sD9Q= +go.mau.fi/whatsmeow v0.0.0-20250807072145-72ce90b82194/go.mod h1:ltDTXUgOAT7LcFKp11H+5S7UY7+xHBMGzNJcv3dLHGk= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= From c160ecbf16b9c63fb26a0cd55ec78631b5a7d7b2 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 11 Aug 2025 17:17:32 +0300 Subject: [PATCH 097/276] handlewhatsapp: fix lid rerouting for undecryptable messages --- go.mod | 2 +- go.sum | 4 +-- pkg/connector/handlewhatsapp.go | 49 ++++++++++++++++++--------------- 3 files changed, 30 insertions(+), 25 deletions(-) diff --git a/go.mod b/go.mod index b5c59f2..172dd54 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.8.9-0.20250723171559-474867266038 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250807072145-72ce90b82194 + go.mau.fi/whatsmeow v0.0.0-20250811141640-b804d10c54c2 golang.org/x/image v0.29.0 golang.org/x/net v0.42.0 golang.org/x/sync v0.16.0 diff --git a/go.sum b/go.sum index adb9935..ad1b4eb 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ go.mau.fi/util v0.8.9-0.20250723171559-474867266038 h1:RVL8TVaYc3LTBBopfjCNDtD+6 go.mau.fi/util v0.8.9-0.20250723171559-474867266038/go.mod h1:GZZp5f9r2MgEu4GDvtB0XxCF7i6Z7Z8fM0w9a5oZH3Y= 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-20250807072145-72ce90b82194 h1:/ow/oKzvxxwJEyCJ4bq8U7W2yar2f0HSq89yto+sD9Q= -go.mau.fi/whatsmeow v0.0.0-20250807072145-72ce90b82194/go.mod h1:ltDTXUgOAT7LcFKp11H+5S7UY7+xHBMGzNJcv3dLHGk= +go.mau.fi/whatsmeow v0.0.0-20250811141640-b804d10c54c2 h1:pPht2EbpwrDKrMKW7t67tpx+6BsFcJyIPy5R45Bu0dk= +go.mau.fi/whatsmeow v0.0.0-20250811141640-b804d10c54c2/go.mod h1:ltDTXUgOAT7LcFKp11H+5S7UY7+xHBMGzNJcv3dLHGk= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index e8a0d5a..3e22f4e 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -85,7 +85,7 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) (success bool) { case *events.ChatPresence: wa.handleWAChatPresence(ctx, evt) case *events.UndecryptableMessage: - success = wa.handleWAUndecryptableMessage(evt) + success = wa.handleWAUndecryptableMessage(ctx, evt) case *events.CallOffer: success = wa.handleWACallStart(ctx, evt.GroupJID, evt.CallCreator, evt.CallID, "", evt.Timestamp) @@ -247,39 +247,43 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) (success bool) { return } -func (wa *WhatsAppClient) handleWAMessage(ctx context.Context, evt *events.Message) (success bool) { - success = true - if evt.Info.Chat.Server == types.HiddenUserServer && evt.Info.Sender.ToNonAD() == evt.Info.Chat && evt.Info.SenderAlt.Server == types.DefaultUserServer { +func (wa *WhatsAppClient) rerouteWAMessage(ctx context.Context, info *types.MessageInfo) { + if info.Chat.Server == types.HiddenUserServer && info.Sender.ToNonAD() == info.Chat && info.SenderAlt.Server == types.DefaultUserServer { wa.UserLogin.Log.Debug(). - Stringer("lid", evt.Info.Sender). - Stringer("pn", evt.Info.SenderAlt). - Str("message_id", evt.Info.ID). + Stringer("lid", info.Sender). + Stringer("pn", info.SenderAlt). + Str("message_id", info.ID). Msg("Forced LID DM sender to phone number in incoming message") - evt.Info.Sender, evt.Info.SenderAlt = evt.Info.SenderAlt, evt.Info.Sender - evt.Info.Chat = evt.Info.Sender.ToNonAD() - } else if evt.Info.Chat.Server == types.HiddenUserServer && evt.Info.IsFromMe && evt.Info.RecipientAlt.Server == types.DefaultUserServer { + info.Sender, info.SenderAlt = info.SenderAlt, info.Sender + info.Chat = info.Sender.ToNonAD() + } else if info.Chat.Server == types.HiddenUserServer && info.IsFromMe && info.RecipientAlt.Server == types.DefaultUserServer { wa.UserLogin.Log.Debug(). - Stringer("lid", evt.Info.Chat). - Stringer("pn", evt.Info.RecipientAlt). - Str("message_id", evt.Info.ID). + Stringer("lid", info.Chat). + Stringer("pn", info.RecipientAlt). + Str("message_id", info.ID). Msg("Forced LID DM sender to phone number in own message sent from another device") - evt.Info.Chat = evt.Info.RecipientAlt.ToNonAD() - } else if evt.Info.Sender.Server == types.BotServer && evt.Info.Chat.Server == types.HiddenUserServer { - chatPN, err := wa.Device.LIDs.GetPNForLID(ctx, evt.Info.Chat) + 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) if err != nil { wa.UserLogin.Log.Err(err). - Str("message_id", evt.Info.ID). - Stringer("lid", evt.Info.Chat). + Str("message_id", info.ID). + Stringer("lid", info.Chat). Msg("Failed to get phone number of DM for incoming bot message") } else if !chatPN.IsEmpty() { wa.UserLogin.Log.Debug(). - Stringer("lid", evt.Info.Chat). + Stringer("lid", info.Chat). Stringer("pn", chatPN). - Str("message_id", evt.Info.ID). + Str("message_id", info.ID). Msg("Forced LID chat to phone number in bot message") - evt.Info.Chat = chatPN + info.Chat = chatPN } } +} + +func (wa *WhatsAppClient) handleWAMessage(ctx context.Context, evt *events.Message) (success bool) { + success = true + wa.rerouteWAMessage(ctx, &evt.Info) wa.UserLogin.Log.Trace(). Any("info", evt.Info). Any("payload", evt.Message). @@ -349,7 +353,8 @@ func (wa *WhatsAppClient) handleWAMessage(ctx context.Context, evt *events.Messa return res.Success } -func (wa *WhatsAppClient) handleWAUndecryptableMessage(evt *events.UndecryptableMessage) bool { +func (wa *WhatsAppClient) handleWAUndecryptableMessage(ctx context.Context, evt *events.UndecryptableMessage) bool { + wa.rerouteWAMessage(ctx, &evt.Info) wa.UserLogin.Log.Debug(). Any("info", evt.Info). Bool("unavailable", evt.IsUnavailable). From b92fb2033666c46bb453abce3240617736c12922 Mon Sep 17 00:00:00 2001 From: Kishan Bagaria <1093313+KishanBagaria@users.noreply.github.com> Date: Fri, 15 Aug 2025 19:29:44 +0530 Subject: [PATCH 098/276] msgconv/from-whatsapp: add better placeholder for pinning messages (#825) --- pkg/msgconv/from-whatsapp.go | 2 ++ pkg/msgconv/wa-misc.go | 15 +++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/pkg/msgconv/from-whatsapp.go b/pkg/msgconv/from-whatsapp.go index 401a19d..e6f8d72 100644 --- a/pkg/msgconv/from-whatsapp.go +++ b/pkg/msgconv/from-whatsapp.go @@ -175,6 +175,8 @@ func (mc *MessageConverter) ToMatrix( part, contextInfo = mc.convertPollUpdateMessage(ctx, info, waMsg.PollUpdateMessage) case waMsg.EventMessage != nil: part, contextInfo = mc.convertEventMessage(ctx, waMsg.EventMessage) + case waMsg.PinInChatMessage != nil: + part, contextInfo = mc.convertPinInChatMessage(ctx, waMsg.PinInChatMessage) case waMsg.ImageMessage != nil: part, contextInfo = mc.convertMediaMessage(ctx, waMsg.ImageMessage, "photo", info, isViewOnce, previouslyConvertedPart) case waMsg.StickerMessage != nil: diff --git a/pkg/msgconv/wa-misc.go b/pkg/msgconv/wa-misc.go index 2b11542..24454c6 100644 --- a/pkg/msgconv/wa-misc.go +++ b/pkg/msgconv/wa-misc.go @@ -214,3 +214,18 @@ func (mc *MessageConverter) convertEventMessage(ctx context.Context, msg *waE2E. Content: &content, }, msg.GetContextInfo() } + +func (mc *MessageConverter) convertPinInChatMessage(ctx context.Context, msg *waE2E.PinInChatMessage) (*bridgev2.ConvertedMessagePart, *waE2E.ContextInfo) { + body := "Pinned a message" + if msg.GetType() == waE2E.PinInChatMessage_UNPIN_FOR_ALL { + body = "Unpinned a message" + } + + return &bridgev2.ConvertedMessagePart{ + Type: event.EventMessage, + Content: &event.MessageEventContent{ + MsgType: event.MsgNotice, + Body: body, + }, + }, nil +} From f71349fcc433963cd17b5b7591ec70b02e6aef56 Mon Sep 17 00:00:00 2001 From: Kishan Bagaria <1093313+KishanBagaria@users.noreply.github.com> Date: Fri, 15 Aug 2025 20:00:47 +0530 Subject: [PATCH 099/276] msgconv/from-whatsapp: add better placeholder for kept messages (#826) --- pkg/msgconv/from-whatsapp.go | 2 ++ pkg/msgconv/wa-misc.go | 15 +++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/pkg/msgconv/from-whatsapp.go b/pkg/msgconv/from-whatsapp.go index e6f8d72..07805d6 100644 --- a/pkg/msgconv/from-whatsapp.go +++ b/pkg/msgconv/from-whatsapp.go @@ -177,6 +177,8 @@ func (mc *MessageConverter) ToMatrix( part, contextInfo = mc.convertEventMessage(ctx, waMsg.EventMessage) case waMsg.PinInChatMessage != nil: part, contextInfo = mc.convertPinInChatMessage(ctx, waMsg.PinInChatMessage) + case waMsg.KeepInChatMessage != nil: + part, contextInfo = mc.convertKeepInChatMessage(ctx, waMsg.KeepInChatMessage) case waMsg.ImageMessage != nil: part, contextInfo = mc.convertMediaMessage(ctx, waMsg.ImageMessage, "photo", info, isViewOnce, previouslyConvertedPart) case waMsg.StickerMessage != nil: diff --git a/pkg/msgconv/wa-misc.go b/pkg/msgconv/wa-misc.go index 24454c6..ef5fc10 100644 --- a/pkg/msgconv/wa-misc.go +++ b/pkg/msgconv/wa-misc.go @@ -229,3 +229,18 @@ func (mc *MessageConverter) convertPinInChatMessage(ctx context.Context, msg *wa }, }, nil } + +func (mc *MessageConverter) convertKeepInChatMessage(ctx context.Context, msg *waE2E.KeepInChatMessage) (*bridgev2.ConvertedMessagePart, *waE2E.ContextInfo) { + body := "Kept a message" + if msg.GetKeepType() == waE2E.KeepType_UNDO_KEEP_FOR_ALL { + body = "Unkept a message" + } + + return &bridgev2.ConvertedMessagePart{ + Type: event.EventMessage, + Content: &event.MessageEventContent{ + MsgType: event.MsgNotice, + Body: body, + }, + }, nil +} From ec57ea91aaf2d1899130b5866ed1ffe2a8758808 Mon Sep 17 00:00:00 2001 From: Kishan Bagaria <1093313+KishanBagaria@users.noreply.github.com> Date: Fri, 15 Aug 2025 20:29:57 +0530 Subject: [PATCH 100/276] msgconv/from-whatsapp: add better support for meta ai responses (#827) --- pkg/msgconv/from-whatsapp.go | 2 ++ pkg/msgconv/wa-misc.go | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/pkg/msgconv/from-whatsapp.go b/pkg/msgconv/from-whatsapp.go index 07805d6..de14928 100644 --- a/pkg/msgconv/from-whatsapp.go +++ b/pkg/msgconv/from-whatsapp.go @@ -179,6 +179,8 @@ func (mc *MessageConverter) ToMatrix( part, contextInfo = mc.convertPinInChatMessage(ctx, waMsg.PinInChatMessage) case waMsg.KeepInChatMessage != nil: part, contextInfo = mc.convertKeepInChatMessage(ctx, waMsg.KeepInChatMessage) + case waMsg.RichResponseMessage != nil: + part, contextInfo = mc.convertRichResponseMessage(ctx, waMsg.RichResponseMessage) case waMsg.ImageMessage != nil: part, contextInfo = mc.convertMediaMessage(ctx, waMsg.ImageMessage, "photo", info, isViewOnce, previouslyConvertedPart) case waMsg.StickerMessage != nil: diff --git a/pkg/msgconv/wa-misc.go b/pkg/msgconv/wa-misc.go index ef5fc10..8865215 100644 --- a/pkg/msgconv/wa-misc.go +++ b/pkg/msgconv/wa-misc.go @@ -244,3 +244,24 @@ func (mc *MessageConverter) convertKeepInChatMessage(ctx context.Context, msg *w }, }, nil } + +func (mc *MessageConverter) convertRichResponseMessage(ctx context.Context, msg *waE2E.AIRichResponseMessage) (*bridgev2.ConvertedMessagePart, *waE2E.ContextInfo) { + var body strings.Builder + + for i, submsg := range msg.GetSubmessages() { + if submsg.GetMessageType() == waE2E.AIRichResponseMessage_AI_RICH_RESPONSE_TEXT { + if i > 0 { + body.WriteString("\n") + } + body.WriteString(submsg.GetMessageText()) + } + } + + return &bridgev2.ConvertedMessagePart{ + Type: event.EventMessage, + Content: &event.MessageEventContent{ + MsgType: event.MsgText, + Body: body.String(), + }, + }, msg.GetContextInfo() +} From 59068ca5fc95cc5c711dfe83f51d1ed16546543b Mon Sep 17 00:00:00 2001 From: Kishan Bagaria <1093313+KishanBagaria@users.noreply.github.com> Date: Fri, 15 Aug 2025 20:49:41 +0530 Subject: [PATCH 101/276] msgconv/from-whatsapp: fix markdown support in meta ai responses (#828) --- pkg/msgconv/wa-misc.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pkg/msgconv/wa-misc.go b/pkg/msgconv/wa-misc.go index 8865215..6d1bca1 100644 --- a/pkg/msgconv/wa-misc.go +++ b/pkg/msgconv/wa-misc.go @@ -257,11 +257,9 @@ func (mc *MessageConverter) convertRichResponseMessage(ctx context.Context, msg } } + content := format.RenderMarkdown(body.String(), true, false) return &bridgev2.ConvertedMessagePart{ - Type: event.EventMessage, - Content: &event.MessageEventContent{ - MsgType: event.MsgText, - Body: body.String(), - }, + Type: event.EventMessage, + Content: &content, }, msg.GetContextInfo() } From 4904f5fff3113402b829a339f0a58c1040bc640b Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sat, 16 Aug 2025 14:02:19 +0300 Subject: [PATCH 102/276] startchat: allow bot and lid mxids in validateIdentifier --- pkg/connector/startchat.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/connector/startchat.go b/pkg/connector/startchat.go index 039fef7..8543bfa 100644 --- a/pkg/connector/startchat.go +++ b/pkg/connector/startchat.go @@ -55,6 +55,8 @@ func looksEmaily(str string) bool { func (wa *WhatsAppClient) validateIdentifer(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) { + return waid.ParseUserID(networkid.UserID(number)), nil } if strings.HasSuffix(number, "@"+types.DefaultUserServer) { jid, _ := types.ParseJID(number) From 48a42719de6e9b05f2ba258742bd6bfc69ad050f Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sat, 16 Aug 2025 14:04:26 +0300 Subject: [PATCH 103/276] Bump version to v0.12.4 --- .github/workflows/go.yml | 4 +-- .pre-commit-config.yaml | 4 +-- CHANGELOG.md | 18 ++++++++++++ cmd/mautrix-whatsapp/main.go | 2 +- go.mod | 32 ++++++++++----------- go.sum | 56 ++++++++++++++++++------------------ 6 files changed, 67 insertions(+), 49 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index e881884..ae5f5aa 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -11,8 +11,8 @@ jobs: strategy: fail-fast: false matrix: - go-version: ["1.23", "1.24"] - name: Lint ${{ matrix.go-version == '1.24' && '(latest)' || '(old)' }} + go-version: ["1.24", "1.25"] + name: Lint ${{ matrix.go-version == '1.25' && '(latest)' || '(old)' }} steps: - uses: actions/checkout@v4 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b8d2d93..a48f8b8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + rev: v6.0.0 hooks: - id: trailing-whitespace exclude_types: [markdown] @@ -9,7 +9,7 @@ repos: - id: check-added-large-files - repo: https://github.com/tekwizely/pre-commit-golang - rev: v1.0.0-rc.1 + rev: v1.0.0-rc.2 hooks: - id: go-imports-repo args: diff --git a/CHANGELOG.md b/CHANGELOG.md index c9a9983..5f9f443 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +# v0.12.4 (2025-08-16) + +* Deprecated legacy provisioning API. The `/_matrix/provision/v1` endpoints will + be deleted in the next release. +* Bumped minimum Go version to 1.24. +* Added support for bridging HD dual uploads from WhatsApp into edits on Matrix. +* Added better placeholders for pin and keep messages from WhatsApp. +* Fixed bridging animated webp stickers to WhatsApp. + * Note that non-square stickers may appear corrupted on native clients. + The bridge will not automatically add padding to animated stickers like it + does for static ones. +* Fixed avatar changes not reflecting on both the LID and phone number ghost of + a given user in certain cases. +* Fixed first message after group LID migration still using the phone number + ghost. +* Fixed bot messages in DMs being split into another portal room. +* Fixed new group members not having a phone number name in some cases. + # v0.12.3 (2025-07-16) * Further improved support for `@lid` users. diff --git a/cmd/mautrix-whatsapp/main.go b/cmd/mautrix-whatsapp/main.go index fc0364d..33dc865 100644 --- a/cmd/mautrix-whatsapp/main.go +++ b/cmd/mautrix-whatsapp/main.go @@ -21,7 +21,7 @@ var m = mxmain.BridgeMain{ Name: "mautrix-whatsapp", URL: "https://github.com/mautrix/whatsapp", Description: "A Matrix-WhatsApp puppeting bridge.", - Version: "0.12.3", + Version: "0.12.4", Connector: c, } diff --git a/go.mod b/go.mod index 172dd54..1ddfc3b 100644 --- a/go.mod +++ b/go.mod @@ -1,22 +1,22 @@ module go.mau.fi/mautrix-whatsapp -go 1.23.0 +go 1.24.0 -toolchain go1.24.5 +toolchain go1.25.0 require ( github.com/gorilla/websocket v1.5.0 github.com/lib/pq v1.10.9 github.com/rs/zerolog v1.34.0 - go.mau.fi/util v0.8.9-0.20250723171559-474867266038 + go.mau.fi/util v0.9.0 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250811141640-b804d10c54c2 - golang.org/x/image v0.29.0 - golang.org/x/net v0.42.0 + go.mau.fi/whatsmeow v0.0.0-20250816105909-9a955523cf0c + golang.org/x/image v0.30.0 + golang.org/x/net v0.43.0 golang.org/x/sync v0.16.0 - google.golang.org/protobuf v1.36.6 + google.golang.org/protobuf v1.36.7 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.24.3-0.20250801084753-196164ed6749 + maunium.net/go/mautrix v0.25.0 ) require ( @@ -27,8 +27,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.28 // indirect - github.com/petermattis/goid v0.0.0-20250721140440-ea1c0173183e // indirect + github.com/mattn/go-sqlite3 v1.14.32 // indirect + github.com/petermattis/goid v0.0.0-20250813065127-a731cc31b4fe // 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 @@ -36,13 +36,13 @@ require ( github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/sjson v1.2.5 // indirect - github.com/yuin/goldmark v1.7.12 // indirect + github.com/yuin/goldmark v1.7.13 // indirect go.mau.fi/libsignal v0.2.0 // indirect - go.mau.fi/zeroconfig v0.1.3 // indirect - golang.org/x/crypto v0.40.0 // indirect - golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 // indirect - golang.org/x/sys v0.34.0 // indirect - golang.org/x/text v0.27.0 // indirect + go.mau.fi/zeroconfig v0.2.0 // indirect + golang.org/x/crypto v0.41.0 // indirect + golang.org/x/exp v0.0.0-20250813145105-42675adae3e6 // indirect + golang.org/x/sys v0.35.0 // indirect + golang.org/x/text v0.28.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 ad1b4eb..5abfb45 100644 --- a/go.sum +++ b/go.sum @@ -32,10 +32,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.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A= -github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/petermattis/goid v0.0.0-20250721140440-ea1c0173183e h1:D0bJD+4O3G4izvrQUmzCL80zazlN7EwJ0PPDhpJWC/I= -github.com/petermattis/goid v0.0.0-20250721140440-ea1c0173183e/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +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-20250813065127-a731cc31b4fe h1:vHpqOnPlnkba8iSxU4j/CvDSS9J4+F4473esQsYLGoE= +github.com/petermattis/goid v0.0.0-20250813065127-a731cc31b4fe/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= @@ -61,37 +61,37 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= -github.com/yuin/goldmark v1.7.12 h1:YwGP/rrea2/CnCtUHgjuolG/PnMxdQtPMO5PvaE2/nY= -github.com/yuin/goldmark v1.7.12/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= +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.8.9-0.20250723171559-474867266038 h1:RVL8TVaYc3LTBBopfjCNDtD+6eZks0O+qgXN/9hsz7k= -go.mau.fi/util v0.8.9-0.20250723171559-474867266038/go.mod h1:GZZp5f9r2MgEu4GDvtB0XxCF7i6Z7Z8fM0w9a5oZH3Y= +go.mau.fi/util v0.9.0 h1:ya3s3pX+Y8R2fgp0DbE7a0o3FwncoelDX5iyaeVE8ls= +go.mau.fi/util v0.9.0/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-20250811141640-b804d10c54c2 h1:pPht2EbpwrDKrMKW7t67tpx+6BsFcJyIPy5R45Bu0dk= -go.mau.fi/whatsmeow v0.0.0-20250811141640-b804d10c54c2/go.mod h1:ltDTXUgOAT7LcFKp11H+5S7UY7+xHBMGzNJcv3dLHGk= -go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= -go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= -golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= -golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= -golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 h1:R9PFI6EUdfVKgwKjZef7QIwGcBKu86OEFpJ9nUEP2l4= -golang.org/x/exp v0.0.0-20250718183923-645b1fa84792/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc= -golang.org/x/image v0.29.0 h1:HcdsyR4Gsuys/Axh0rDEmlBmB68rW1U9BUdB3UVHsas= -golang.org/x/image v0.29.0/go.mod h1:RVJROnf3SLK8d26OW91j4FrIHGbsJ8QnbEocVTOWQDA= -golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= -golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= +go.mau.fi/whatsmeow v0.0.0-20250816105909-9a955523cf0c h1:5qVuYMjp1NZHfuMHTxBuITujkPmrXnxOZ9si16i3MzM= +go.mau.fi/whatsmeow v0.0.0-20250816105909-9a955523cf0c/go.mod h1:xD0DR3s4T6PDd3BzgQG05AzLWxdKCmnvdCP3UuQvn9w= +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= +golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= +golang.org/x/exp v0.0.0-20250813145105-42675adae3e6 h1:SbTAbRFnd5kjQXbczszQ0hdk3ctwYf3qBNH9jIsGclE= +golang.org/x/exp v0.0.0-20250813145105-42675adae3e6/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4= +golang.org/x/image v0.30.0 h1:jD5RhkmVAnjqaCUXfbGBrn3lpxbknfN9w2UhHHU+5B4= +golang.org/x/image v0.30.0/go.mod h1:SAEUTxCCMWSrJcCy/4HwavEsfZZJlYxeHLc6tTiAe/c= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= +golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 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.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= -golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= -golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= -google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= -google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A= +google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= 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= @@ -101,5 +101,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.24.3-0.20250801084753-196164ed6749 h1:eEIsN9CufSHs4SeRjUwIpJo3n8QaX0rfO2sEERjPkKY= -maunium.net/go/mautrix v0.24.3-0.20250801084753-196164ed6749/go.mod h1:KrE/TdIeAo6cfAUICmbaiZ18UgHimTOozAGOrioK9SU= +maunium.net/go/mautrix v0.25.0 h1:dhYoXIXSxI9A+kEPwBceuRP0wcpho15dVLucUF8k2eE= +maunium.net/go/mautrix v0.25.0/go.mod h1:pDd6Ppg+1PbWrw/rg4ZQQfVYZICRGzH+DcliZ/BODvU= From b3903f6cb5e10294689278baff27ed07b19224f3 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 19 Aug 2025 17:44:36 +0300 Subject: [PATCH 104/276] docker: install lottieconverter Fixes #831 --- Dockerfile | 2 +- Dockerfile.ci | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 32d7a6d..3b71e4f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,7 @@ FROM alpine:3.22 ENV UID=1337 \ GID=1337 -RUN apk add --no-cache ffmpeg su-exec ca-certificates olm bash jq yq curl +RUN apk add --no-cache ffmpeg su-exec ca-certificates olm bash jq curl yq-go lottieconverter COPY --from=builder /build/mautrix-whatsapp /usr/bin/mautrix-whatsapp COPY --from=builder /build/docker-run.sh /docker-run.sh diff --git a/Dockerfile.ci b/Dockerfile.ci index 28c7cfb..9b7189d 100644 --- a/Dockerfile.ci +++ b/Dockerfile.ci @@ -3,7 +3,7 @@ FROM alpine:3.22 ENV UID=1337 \ GID=1337 -RUN apk add --no-cache ffmpeg su-exec ca-certificates bash jq curl yq-go +RUN apk add --no-cache ffmpeg su-exec ca-certificates bash jq curl yq-go lottieconverter ARG EXECUTABLE=./mautrix-whatsapp COPY $EXECUTABLE /usr/bin/mautrix-whatsapp From dca368d02e260d71654f674ccffd083fb07e0e61 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 21 Aug 2025 17:03:30 +0300 Subject: [PATCH 105/276] msgconv/from-whatsapp: don't use different logins for mentions in split portals --- pkg/msgconv/from-whatsapp.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/msgconv/from-whatsapp.go b/pkg/msgconv/from-whatsapp.go index de14928..2f68b34 100644 --- a/pkg/msgconv/from-whatsapp.go +++ b/pkg/msgconv/from-whatsapp.go @@ -89,8 +89,9 @@ func (mc *MessageConverter) getBasicUserInfo(ctx context.Context, user types.JID } } if !pnJID.IsEmpty() { + portal := getPortal(ctx) login := mc.Bridge.GetCachedUserLoginByID(waid.MakeUserLoginID(pnJID)) - if login != nil { + if login != nil && (portal.Receiver == "" || portal.Receiver == login.ID) { return login.UserMXID, ghost.Name, nil } } From 7ff2f9f20665a52310a61d172822e359fe18a660 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 25 Aug 2025 14:47:07 +0300 Subject: [PATCH 106/276] msgconv/from-matrix: don't allow sending png to whatsapp --- pkg/connector/capabilities.go | 6 +++--- pkg/msgconv/from-matrix.go | 21 ++++++++++----------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/pkg/connector/capabilities.go b/pkg/connector/capabilities.go index c54c378..81e77ec 100644 --- a/pkg/connector/capabilities.go +++ b/pkg/connector/capabilities.go @@ -23,7 +23,7 @@ func (wa *WhatsAppConnector) GetCapabilities() *bridgev2.NetworkGeneralCapabilit } func (wa *WhatsAppConnector) GetBridgeInfoVersion() (info, caps int) { - return 1, 2 + return 1, 3 } const WAMaxFileSize = 2000 * 1024 * 1024 @@ -38,7 +38,7 @@ func supportedIfFFmpeg() event.CapabilitySupportLevel { } func capID() string { - base := "fi.mau.whatsapp.capabilities.2025_06_03" + base := "fi.mau.whatsapp.capabilities.2025_08_25" if ffmpeg.Supported() { return base + "+ffmpeg" } @@ -66,8 +66,8 @@ var whatsappCaps = &event.RoomFeatures{ File: map[event.CapabilityMsgType]*event.FileFeatures{ event.MsgImage: { MimeTypes: map[string]event.CapabilitySupportLevel{ - "image/png": event.CapLevelFullySupported, "image/jpeg": event.CapLevelFullySupported, + "image/png": event.CapLevelPartialSupport, "image/webp": event.CapLevelPartialSupport, "image/gif": supportedIfFFmpeg(), }, diff --git a/pkg/msgconv/from-matrix.go b/pkg/msgconv/from-matrix.go index a71824a..3cca335 100644 --- a/pkg/msgconv/from-matrix.go +++ b/pkg/msgconv/from-matrix.go @@ -23,7 +23,7 @@ import ( "fmt" "image" "image/color" - "image/png" + "image/jpeg" "net/http" "slices" "strconv" @@ -37,7 +37,6 @@ import ( "go.mau.fi/whatsmeow" "go.mau.fi/whatsmeow/proto/waE2E" "go.mau.fi/whatsmeow/types" - "golang.org/x/image/webp" "google.golang.org/protobuf/proto" "maunium.net/go/mautrix/bridgev2" "maunium.net/go/mautrix/bridgev2/database" @@ -360,18 +359,18 @@ func (img *PaddedImage) At(x, y int) color.Color { return img.Image.At(x-img.OffsetX, y-img.OffsetY) } -func (mc *MessageConverter) convertWebPtoPNG(webpImage []byte) ([]byte, error) { - webpDecoded, err := webp.Decode(bytes.NewReader(webpImage)) +func (mc *MessageConverter) convertToJPEG(webpImage []byte) ([]byte, error) { + decoded, _, err := image.Decode(bytes.NewReader(webpImage)) if err != nil { return nil, fmt.Errorf("failed to decode webp image: %w", err) } - var pngBuffer bytes.Buffer - if err = png.Encode(&pngBuffer, webpDecoded); err != nil { + var jpgBuffer bytes.Buffer + if err = jpeg.Encode(&jpgBuffer, decoded, &jpeg.Options{Quality: 80}); err != nil { return nil, fmt.Errorf("failed to encode png image: %w", err) } - return pngBuffer.Bytes(), nil + return jpgBuffer.Bytes(), nil } func (mc *MessageConverter) convertToWebP(img []byte) ([]byte, int, error) { @@ -454,14 +453,14 @@ func (mc *MessageConverter) reuploadFileToWhatsApp( case event.MsgImage: mediaType = whatsmeow.MediaImage switch mime { - case "image/jpeg", "image/png": + case "image/jpeg": // allowed - case "image/webp": - data, err = mc.convertWebPtoPNG(data) + case "image/webp", "image/png": + data, err = mc.convertToJPEG(data) if err != nil { return nil, nil, "image/webp", fmt.Errorf("%w (webp to png): %s", bridgev2.ErrMediaConvertFailed, err) } - mime = "image/png" + mime = "image/jpeg" default: return nil, nil, mime, fmt.Errorf("%w %s in image message", bridgev2.ErrUnsupportedMediaType, mime) } From c7602c02c908efcad2f5c7eb8267564a54f51e52 Mon Sep 17 00:00:00 2001 From: Kishan Bagaria <1093313+KishanBagaria@users.noreply.github.com> Date: Mon, 25 Aug 2025 17:26:55 +0530 Subject: [PATCH 107/276] all: enable mautrix-go's new disappearing message features (#829) --- go.mod | 4 ++-- go.sum | 8 ++++---- pkg/connector/capabilities.go | 12 ++++++++++-- pkg/connector/chatinfo.go | 6 +++--- pkg/msgconv/from-matrix.go | 23 +++++++++++++++-------- pkg/msgconv/from-whatsapp.go | 3 +-- pkg/msgconv/matrixpoll.go | 2 +- pkg/msgconv/wa-misc.go | 2 +- 8 files changed, 37 insertions(+), 23 deletions(-) diff --git a/go.mod b/go.mod index 1ddfc3b..a389b3d 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( golang.org/x/sync v0.16.0 google.golang.org/protobuf v1.36.7 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.25.0 + maunium.net/go/mautrix v0.25.1-0.20250823214755-7e07700a6943 ) require ( @@ -40,7 +40,7 @@ require ( go.mau.fi/libsignal v0.2.0 // indirect go.mau.fi/zeroconfig v0.2.0 // indirect golang.org/x/crypto v0.41.0 // indirect - golang.org/x/exp v0.0.0-20250813145105-42675adae3e6 // indirect + golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b // indirect golang.org/x/sys v0.35.0 // indirect golang.org/x/text v0.28.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect diff --git a/go.sum b/go.sum index 5abfb45..5eaf2d1 100644 --- a/go.sum +++ b/go.sum @@ -75,8 +75,8 @@ 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= golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= -golang.org/x/exp v0.0.0-20250813145105-42675adae3e6 h1:SbTAbRFnd5kjQXbczszQ0hdk3ctwYf3qBNH9jIsGclE= -golang.org/x/exp v0.0.0-20250813145105-42675adae3e6/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4= +golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b h1:DXr+pvt3nC887026GRP39Ej11UATqWDmWuS99x26cD0= +golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4= golang.org/x/image v0.30.0 h1:jD5RhkmVAnjqaCUXfbGBrn3lpxbknfN9w2UhHHU+5B4= golang.org/x/image v0.30.0/go.mod h1:SAEUTxCCMWSrJcCy/4HwavEsfZZJlYxeHLc6tTiAe/c= golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= @@ -101,5 +101,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.0 h1:dhYoXIXSxI9A+kEPwBceuRP0wcpho15dVLucUF8k2eE= -maunium.net/go/mautrix v0.25.0/go.mod h1:pDd6Ppg+1PbWrw/rg4ZQQfVYZICRGzH+DcliZ/BODvU= +maunium.net/go/mautrix v0.25.1-0.20250823214755-7e07700a6943 h1:qNRr7dDDJhGUMTqkp4p1EJ18SL0yMKzB1pdzSZ2XUWo= +maunium.net/go/mautrix v0.25.1-0.20250823214755-7e07700a6943/go.mod h1:pDd6Ppg+1PbWrw/rg4ZQQfVYZICRGzH+DcliZ/BODvU= diff --git a/pkg/connector/capabilities.go b/pkg/connector/capabilities.go index 81e77ec..f850cf0 100644 --- a/pkg/connector/capabilities.go +++ b/pkg/connector/capabilities.go @@ -23,7 +23,7 @@ func (wa *WhatsAppConnector) GetCapabilities() *bridgev2.NetworkGeneralCapabilit } func (wa *WhatsAppConnector) GetBridgeInfoVersion() (info, caps int) { - return 1, 3 + return 1, 4 } const WAMaxFileSize = 2000 * 1024 * 1024 @@ -38,7 +38,7 @@ func supportedIfFFmpeg() event.CapabilitySupportLevel { } func capID() string { - base := "fi.mau.whatsapp.capabilities.2025_08_25" + base := "fi.mau.whatsapp.capabilities.2025_08_25+1" if ffmpeg.Supported() { return base + "+ffmpeg" } @@ -148,6 +148,14 @@ var whatsappCaps = &event.RoomFeatures{ ReactionCount: 1, ReadReceipts: true, TypingNotifications: true, + DisappearingTimer: &event.DisappearingTimerCapability{ + Types: []event.DisappearingType{event.DisappearingTypeAfterSend}, + Timers: []jsontime.Milliseconds{ + jsontime.MS(24 * time.Hour), // 24 hours + jsontime.MS(7 * 24 * time.Hour), // 7 days + jsontime.MS(90 * 24 * time.Hour), // 90 days + }, + }, } var whatsappCAGCaps *event.RoomFeatures diff --git a/pkg/connector/chatinfo.go b/pkg/connector/chatinfo.go index b83b1f7..57a18ac 100644 --- a/pkg/connector/chatinfo.go +++ b/pkg/connector/chatinfo.go @@ -122,7 +122,7 @@ func (wa *WhatsAppClient) applyHistoryInfo(info *bridgev2.ChatInfo, conv *wadb.C } if info.Disappear == nil && ptr.Val(conv.EphemeralExpiration) > 0 { info.Disappear = &database.DisappearingSetting{ - Type: database.DisappearingTypeAfterRead, + Type: event.DisappearingTypeAfterSend, Timer: time.Duration(*conv.EphemeralExpiration) * time.Second, } if conv.EphemeralSettingTimestamp != nil { @@ -255,7 +255,7 @@ func (wa *WhatsAppClient) wrapGroupInfo(ctx context.Context, info *types.GroupIn }, }, Disappear: &database.DisappearingSetting{ - Type: database.DisappearingTypeAfterRead, + Type: event.DisappearingTypeAfterSend, Timer: time.Duration(info.DisappearingTimer) * time.Second, }, ExtraUpdates: extraUpdater, @@ -311,7 +311,7 @@ func (wa *WhatsAppClient) wrapGroupInfoChange(ctx context.Context, evt *events.G } if evt.Ephemeral != nil { changes.Disappear = &database.DisappearingSetting{ - Type: database.DisappearingTypeAfterRead, + Type: event.DisappearingTypeAfterSend, Timer: time.Duration(evt.Ephemeral.DisappearingTimer) * time.Second, } if !evt.Ephemeral.IsEphemeral { diff --git a/pkg/msgconv/from-matrix.go b/pkg/msgconv/from-matrix.go index 3cca335..7fde6a1 100644 --- a/pkg/msgconv/from-matrix.go +++ b/pkg/msgconv/from-matrix.go @@ -28,6 +28,7 @@ import ( "slices" "strconv" "strings" + "time" "github.com/rs/zerolog" "go.mau.fi/util/ffmpeg" @@ -47,7 +48,7 @@ import ( "go.mau.fi/mautrix-whatsapp/pkg/waid" ) -func (mc *MessageConverter) generateContextInfo(ctx context.Context, replyTo *database.Message, portal *bridgev2.Portal) *waE2E.ContextInfo { +func (mc *MessageConverter) generateContextInfo(ctx context.Context, replyTo *database.Message, portal *bridgev2.Portal, perMessageTimer *event.BeeperDisappearingTimer) *waE2E.ContextInfo { contextInfo := &waE2E.ContextInfo{} if replyTo != nil { msgID, err := waid.ParseMessageID(replyTo.ID) @@ -62,12 +63,18 @@ func (mc *MessageConverter) generateContextInfo(ctx context.Context, replyTo *da Msg("Failed to parse reply to message ID") } } - if portal.Disappear.Timer > 0 { - contextInfo.Expiration = ptr.Ptr(uint32(portal.Disappear.Timer.Seconds())) - setAt := portal.Metadata.(*waid.PortalMetadata).DisappearingTimerSetAt - if setAt > 0 { - contextInfo.EphemeralSettingTimestamp = ptr.Ptr(setAt) - } + var timer time.Duration + if perMessageTimer != nil { + timer = perMessageTimer.Timer.Duration + } else { + timer = portal.Disappear.Timer + } + if timer > 0 { + contextInfo.Expiration = ptr.Ptr(uint32(timer.Seconds())) + } + setAt := portal.Metadata.(*waid.PortalMetadata).DisappearingTimerSetAt + if setAt > 0 && contextInfo.Expiration != nil { + contextInfo.EphemeralSettingTimestamp = ptr.Ptr(setAt) } return contextInfo } @@ -88,7 +95,7 @@ func (mc *MessageConverter) ToWhatsApp( } message := &waE2E.Message{} - contextInfo := mc.generateContextInfo(ctx, replyTo, portal) + contextInfo := mc.generateContextInfo(ctx, replyTo, portal, content.BeeperDisappearingTimer) 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 2f68b34..a603efa 100644 --- a/pkg/msgconv/from-whatsapp.go +++ b/pkg/msgconv/from-whatsapp.go @@ -33,7 +33,6 @@ import ( "go.mau.fi/whatsmeow/types" _ "golang.org/x/image/webp" "maunium.net/go/mautrix/bridgev2" - "maunium.net/go/mautrix/bridgev2/database" "maunium.net/go/mautrix/bridgev2/networkid" "maunium.net/go/mautrix/event" "maunium.net/go/mautrix/id" @@ -240,7 +239,7 @@ func (mc *MessageConverter) ToMatrix( } if contextInfo.GetExpiration() > 0 { cm.Disappear.Timer = time.Duration(contextInfo.GetExpiration()) * time.Second - cm.Disappear.Type = database.DisappearingTypeAfterRead + cm.Disappear.Type = event.DisappearingTypeAfterSend } if portal.Disappear.Timer != cm.Disappear.Timer && portal.Metadata.(*waid.PortalMetadata).DisappearingTimerSetAt < contextInfo.GetEphemeralSettingTimestamp() { portal.UpdateDisappearingSetting(ctx, cm.Disappear, intent, info.Timestamp, true, true) diff --git a/pkg/msgconv/matrixpoll.go b/pkg/msgconv/matrixpoll.go index 34040ab..22cc7af 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) + contextInfo := mc.generateContextInfo(ctx, replyTo, portal, nil) var question string question, contextInfo.MentionedJID = mc.msc1767ToWhatsApp(ctx, content.PollStart.Question, content.Mentions) if len(question) == 0 { diff --git a/pkg/msgconv/wa-misc.go b/pkg/msgconv/wa-misc.go index 6d1bca1..f24c9ee 100644 --- a/pkg/msgconv/wa-misc.go +++ b/pkg/msgconv/wa-misc.go @@ -118,7 +118,7 @@ func (mc *MessageConverter) convertEphemeralSettingMessage(ctx context.Context, portal := getPortal(ctx) portalMeta := portal.Metadata.(*waid.PortalMetadata) disappear := database.DisappearingSetting{ - Type: database.DisappearingTypeAfterRead, + Type: event.DisappearingTypeAfterSend, Timer: time.Duration(msg.GetEphemeralExpiration()) * time.Second, } if disappear.Timer == 0 { From 6c5f05ce3622fe644fbc42e11c87b74084c988b0 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 25 Aug 2025 17:22:35 +0300 Subject: [PATCH 108/276] msgconv/from-whatsapp: properly update disappearing message timer in room state --- go.mod | 2 +- go.sum | 4 ++-- pkg/msgconv/from-whatsapp.go | 10 ++++++++-- pkg/msgconv/wa-misc.go | 14 ++++++++------ 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/go.mod b/go.mod index a389b3d..a5dd7e5 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( golang.org/x/sync v0.16.0 google.golang.org/protobuf v1.36.7 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.25.1-0.20250823214755-7e07700a6943 + maunium.net/go/mautrix v0.25.1-0.20250825142325-f860b0e2386a ) require ( diff --git a/go.sum b/go.sum index 5eaf2d1..a7806f9 100644 --- a/go.sum +++ b/go.sum @@ -101,5 +101,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.1-0.20250823214755-7e07700a6943 h1:qNRr7dDDJhGUMTqkp4p1EJ18SL0yMKzB1pdzSZ2XUWo= -maunium.net/go/mautrix v0.25.1-0.20250823214755-7e07700a6943/go.mod h1:pDd6Ppg+1PbWrw/rg4ZQQfVYZICRGzH+DcliZ/BODvU= +maunium.net/go/mautrix v0.25.1-0.20250825142325-f860b0e2386a h1:Qfsfq3BEzLKNsLy+BJIv0kfUvJK6cXKav7R2zyqMsIc= +maunium.net/go/mautrix v0.25.1-0.20250825142325-f860b0e2386a/go.mod h1:pDd6Ppg+1PbWrw/rg4ZQQfVYZICRGzH+DcliZ/BODvU= diff --git a/pkg/msgconv/from-whatsapp.go b/pkg/msgconv/from-whatsapp.go index a603efa..8e05384 100644 --- a/pkg/msgconv/from-whatsapp.go +++ b/pkg/msgconv/from-whatsapp.go @@ -212,7 +212,7 @@ func (mc *MessageConverter) ToMatrix( case waMsg.GroupInviteMessage != nil: 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) + part, contextInfo = mc.convertEphemeralSettingMessage(ctx, waMsg.ProtocolMessage, info.Timestamp) case waMsg.EncCommentMessage != nil: part = failedCommentPart default: @@ -242,7 +242,13 @@ func (mc *MessageConverter) ToMatrix( cm.Disappear.Type = event.DisappearingTypeAfterSend } if portal.Disappear.Timer != cm.Disappear.Timer && portal.Metadata.(*waid.PortalMetadata).DisappearingTimerSetAt < contextInfo.GetEphemeralSettingTimestamp() { - portal.UpdateDisappearingSetting(ctx, cm.Disappear, intent, info.Timestamp, true, true) + portal.UpdateDisappearingSetting(ctx, cm.Disappear, bridgev2.UpdateDisappearingSettingOpts{ + Sender: intent, + Timestamp: info.Timestamp, + Implicit: true, + Save: true, + SendNotice: true, + }) } if contextInfo.GetStanzaID() != "" { pcp, _ := types.ParseJID(contextInfo.GetParticipant()) diff --git a/pkg/msgconv/wa-misc.go b/pkg/msgconv/wa-misc.go index f24c9ee..6120d77 100644 --- a/pkg/msgconv/wa-misc.go +++ b/pkg/msgconv/wa-misc.go @@ -114,7 +114,7 @@ func (mc *MessageConverter) convertGroupInviteMessage(ctx context.Context, info }, msg.GetContextInfo() } -func (mc *MessageConverter) convertEphemeralSettingMessage(ctx context.Context, msg *waE2E.ProtocolMessage) (*bridgev2.ConvertedMessagePart, *waE2E.ContextInfo) { +func (mc *MessageConverter) convertEphemeralSettingMessage(ctx context.Context, msg *waE2E.ProtocolMessage, ts time.Time) (*bridgev2.ConvertedMessagePart, *waE2E.ContextInfo) { portal := getPortal(ctx) portalMeta := portal.Metadata.(*waid.PortalMetadata) disappear := database.DisappearingSetting{ @@ -127,12 +127,14 @@ func (mc *MessageConverter) convertEphemeralSettingMessage(ctx context.Context, dontBridge := portal.Disappear == disappear content := bridgev2.DisappearingMessageNotice(disappear.Timer, false) if msg.EphemeralSettingTimestamp == nil || portalMeta.DisappearingTimerSetAt < msg.GetEphemeralSettingTimestamp() { - portal.Disappear = disappear portalMeta.DisappearingTimerSetAt = msg.GetEphemeralSettingTimestamp() - err := portal.Save(ctx) - if err != nil { - zerolog.Ctx(ctx).Err(err).Msg("Failed to save portal after updating expiration timer") - } + portal.UpdateDisappearingSetting(ctx, disappear, bridgev2.UpdateDisappearingSettingOpts{ + Sender: getIntent(ctx), + Timestamp: ts, + Implicit: false, + Save: true, + SendNotice: false, + }) } else { content.Body += ", but the change was ignored." } From 37f0c4ad1897daf87e110b5c903d08ed06ef1c6c Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 25 Aug 2025 18:15:50 +0300 Subject: [PATCH 109/276] handlematrix: add support for changing disappearing timer --- ROADMAP.md | 8 +++---- go.mod | 4 ++-- go.sum | 8 +++---- pkg/connector/chatinfo.go | 11 ++++++++- pkg/connector/handlematrix.go | 44 ++++++++++++++++++++++++++++++----- 5 files changed, 58 insertions(+), 17 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 91f4371..980606d 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -14,10 +14,10 @@ * [x] Typing notifications * [x] Read receipts * [ ] Power level - * [ ] Membership actions - * [ ] Invite - * [ ] Leave - * [ ] Kick + * [x] Membership actions + * [x] Invite + * [x] Leave + * [x] Kick * [ ] Room metadata changes * [ ] Name * [ ] Avatar diff --git a/go.mod b/go.mod index a5dd7e5..7c59149 100644 --- a/go.mod +++ b/go.mod @@ -10,13 +10,13 @@ require ( github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.0 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250816105909-9a955523cf0c + go.mau.fi/whatsmeow v0.0.0-20250825151841-7b1a3f7e5746 golang.org/x/image v0.30.0 golang.org/x/net v0.43.0 golang.org/x/sync v0.16.0 google.golang.org/protobuf v1.36.7 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.25.1-0.20250825142325-f860b0e2386a + maunium.net/go/mautrix v0.25.1-0.20250825160307-63b654187d40 ) require ( diff --git a/go.sum b/go.sum index a7806f9..80b9434 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ go.mau.fi/util v0.9.0 h1:ya3s3pX+Y8R2fgp0DbE7a0o3FwncoelDX5iyaeVE8ls= go.mau.fi/util v0.9.0/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-20250816105909-9a955523cf0c h1:5qVuYMjp1NZHfuMHTxBuITujkPmrXnxOZ9si16i3MzM= -go.mau.fi/whatsmeow v0.0.0-20250816105909-9a955523cf0c/go.mod h1:xD0DR3s4T6PDd3BzgQG05AzLWxdKCmnvdCP3UuQvn9w= +go.mau.fi/whatsmeow v0.0.0-20250825151841-7b1a3f7e5746 h1:WquxGXJBTlOTkBSltW52rqzRbQDAYD7JJgMli6FiIiY= +go.mau.fi/whatsmeow v0.0.0-20250825151841-7b1a3f7e5746/go.mod h1:xD0DR3s4T6PDd3BzgQG05AzLWxdKCmnvdCP3UuQvn9w= 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= @@ -101,5 +101,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.1-0.20250825142325-f860b0e2386a h1:Qfsfq3BEzLKNsLy+BJIv0kfUvJK6cXKav7R2zyqMsIc= -maunium.net/go/mautrix v0.25.1-0.20250825142325-f860b0e2386a/go.mod h1:pDd6Ppg+1PbWrw/rg4ZQQfVYZICRGzH+DcliZ/BODvU= +maunium.net/go/mautrix v0.25.1-0.20250825160307-63b654187d40 h1:hxHT1z1lixT0s/y/8E6M/OB0D5PwBlNYpZtgKkREIQU= +maunium.net/go/mautrix v0.25.1-0.20250825160307-63b654187d40/go.mod h1:pDd6Ppg+1PbWrw/rg4ZQQfVYZICRGzH+DcliZ/BODvU= diff --git a/pkg/connector/chatinfo.go b/pkg/connector/chatinfo.go index 57a18ac..1a912a0 100644 --- a/pkg/connector/chatinfo.go +++ b/pkg/connector/chatinfo.go @@ -149,7 +149,14 @@ func (wa *WhatsAppClient) wrapDMInfo(ctx context.Context, jid types.JID) *bridge waid.MakeUserID(jid): {EventSender: wa.makeEventSender(ctx, jid)}, waid.MakeUserID(wa.JID): {EventSender: wa.makeEventSender(ctx, wa.JID)}, }, - PowerLevels: nil, + PowerLevels: &bridgev2.PowerLevelOverrides{ + Events: map[event.Type]int{ + event.StateRoomName: 0, + event.StateRoomAvatar: 0, + event.StateTopic: 0, + event.StateBeeperDisappearingTimer: 0, + }, + }, }, Type: ptr.Ptr(database.RoomTypeDM), } @@ -250,6 +257,8 @@ func (wa *WhatsAppClient) wrapGroupInfo(ctx context.Context, info *types.GroupIn event.StateTopic: metaChangePL, event.EventReaction: defaultPL, event.EventRedaction: defaultPL, + + event.StateBeeperDisappearingTimer: metaChangePL, // TODO always allow poll responses }, }, diff --git a/pkg/connector/handlematrix.go b/pkg/connector/handlematrix.go index a97aee7..d271da6 100644 --- a/pkg/connector/handlematrix.go +++ b/pkg/connector/handlematrix.go @@ -24,12 +24,14 @@ import ( ) var ( - _ bridgev2.TypingHandlingNetworkAPI = (*WhatsAppClient)(nil) - _ bridgev2.EditHandlingNetworkAPI = (*WhatsAppClient)(nil) - _ bridgev2.ReactionHandlingNetworkAPI = (*WhatsAppClient)(nil) - _ bridgev2.RedactionHandlingNetworkAPI = (*WhatsAppClient)(nil) - _ bridgev2.ReadReceiptHandlingNetworkAPI = (*WhatsAppClient)(nil) - _ bridgev2.PollHandlingNetworkAPI = (*WhatsAppClient)(nil) + _ bridgev2.TypingHandlingNetworkAPI = (*WhatsAppClient)(nil) + _ bridgev2.EditHandlingNetworkAPI = (*WhatsAppClient)(nil) + _ bridgev2.ReactionHandlingNetworkAPI = (*WhatsAppClient)(nil) + _ bridgev2.RedactionHandlingNetworkAPI = (*WhatsAppClient)(nil) + _ bridgev2.ReadReceiptHandlingNetworkAPI = (*WhatsAppClient)(nil) + _ bridgev2.PollHandlingNetworkAPI = (*WhatsAppClient)(nil) + _ bridgev2.DisappearTimerChangingNetworkAPI = (*WhatsAppClient)(nil) + _ bridgev2.MembershipHandlingNetworkAPI = (*WhatsAppClient)(nil) ) func (wa *WhatsAppClient) HandleMatrixPollStart(ctx context.Context, msg *bridgev2.MatrixPollStart) (*bridgev2.MatrixMessageResponse, error) { @@ -344,6 +346,36 @@ func (wa *WhatsAppClient) HandleMatrixTyping(ctx context.Context, msg *bridgev2. return wa.Client.SendChatPresence(portalJID, chatPresence, mediaPresence) } +var errUnsupportedDisappearingTimer = bridgev2.WrapErrorInStatus(errors.New("invalid value for disappearing timer")).WithErrorAsMessage().WithIsCertain(true).WithSendNotice(true) + +func (wa *WhatsAppClient) HandleMatrixDisappearingTimer(ctx context.Context, msg *bridgev2.MatrixDisappearingTimer) (bool, error) { + portalJID, err := waid.ParsePortalID(msg.Portal.ID) + if err != nil { + return false, err + } + + switch msg.Content.Timer.Duration { + case whatsmeow.DisappearingTimerOff, whatsmeow.DisappearingTimer24Hours, whatsmeow.DisappearingTimer7Days, whatsmeow.DisappearingTimer90Days: + default: + return false, fmt.Errorf("%w (%s)", errUnsupportedDisappearingTimer, msg.Content.Timer.Duration) + } + + settingTS := time.UnixMilli(msg.Event.Timestamp) + err = wa.Client.SetDisappearingTimer(portalJID, msg.Content.Timer.Duration, settingTS) + if err != nil { + return false, err + } + msg.Portal.Metadata.(*waid.PortalMetadata).DisappearingTimerSetAt = settingTS.Unix() + msg.Portal.Disappear = database.DisappearingSetting{ + Type: event.DisappearingTypeAfterSend, + Timer: msg.Content.Timer.Duration, + } + if msg.Portal.Disappear.Timer == 0 { + msg.Portal.Disappear.Type = event.DisappearingTypeNone + } + return true, nil +} + func (wa *WhatsAppClient) HandleMatrixMembership(ctx context.Context, msg *bridgev2.MatrixMembershipChange) (bool, error) { portalJID, err := waid.ParsePortalID(msg.Portal.ID) if err != nil { From 08948811bb0968f5ebccc7ac682aac4134975d51 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 26 Aug 2025 17:07:46 +0300 Subject: [PATCH 110/276] 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 7c59149..3ffe06e 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( golang.org/x/sync v0.16.0 google.golang.org/protobuf v1.36.7 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.25.1-0.20250825160307-63b654187d40 + maunium.net/go/mautrix v0.25.1-0.20250826140716-0345a5356de1 ) require ( diff --git a/go.sum b/go.sum index 80b9434..e591eb1 100644 --- a/go.sum +++ b/go.sum @@ -101,5 +101,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.1-0.20250825160307-63b654187d40 h1:hxHT1z1lixT0s/y/8E6M/OB0D5PwBlNYpZtgKkREIQU= -maunium.net/go/mautrix v0.25.1-0.20250825160307-63b654187d40/go.mod h1:pDd6Ppg+1PbWrw/rg4ZQQfVYZICRGzH+DcliZ/BODvU= +maunium.net/go/mautrix v0.25.1-0.20250826140716-0345a5356de1 h1:8U8dwv4dxhMLrtqjf9LoDuQ6daGj9qdw1/62HIvK2cY= +maunium.net/go/mautrix v0.25.1-0.20250826140716-0345a5356de1/go.mod h1:pDd6Ppg+1PbWrw/rg4ZQQfVYZICRGzH+DcliZ/BODvU= From c033ce0948c1cadde89480d3af4ccffeb98d937d Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 28 Aug 2025 02:29:17 +0300 Subject: [PATCH 111/276] legacy{migrate,provision}: delete Closes #782 Closes #739 --- CHANGELOG.md | 10 + cmd/mautrix-whatsapp/legacymigrate.go | 70 ------ cmd/mautrix-whatsapp/legacymigrate.sql | 215 ----------------- cmd/mautrix-whatsapp/legacyprovision.go | 307 ------------------------ cmd/mautrix-whatsapp/main.go | 28 +-- go.mod | 4 +- go.sum | 4 +- 7 files changed, 15 insertions(+), 623 deletions(-) delete mode 100644 cmd/mautrix-whatsapp/legacymigrate.go delete mode 100644 cmd/mautrix-whatsapp/legacymigrate.sql delete mode 100644 cmd/mautrix-whatsapp/legacyprovision.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f9f443..0b85c5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +# v0.12.5 (unreleased) + +* Removed legacy provisioning API and database legacy migration. + Upgrading directly from versions prior to v0.11.0 is not supported. +* Added support for `com.beeper.disappearing_timer` state event, which stores + the disappearing setting of chats and allows changing the setting from Matrix. +* Added lottieconverter to Docker images to enable converting animated stickers + from WhatsApp. +* Fixed sent PNGs not being rendered on WhatsApp iOS. + # v0.12.4 (2025-08-16) * Deprecated legacy provisioning API. The `/_matrix/provision/v1` endpoints will diff --git a/cmd/mautrix-whatsapp/legacymigrate.go b/cmd/mautrix-whatsapp/legacymigrate.go deleted file mode 100644 index 34bcf64..0000000 --- a/cmd/mautrix-whatsapp/legacymigrate.go +++ /dev/null @@ -1,70 +0,0 @@ -package main - -import ( - _ "embed" - "strings" - - up "go.mau.fi/util/configupgrade" - "go.mau.fi/util/dbutil/litestream" - "maunium.net/go/mautrix/bridgev2/bridgeconfig" -) - -const legacyMigrateRenameTables = ` -ALTER TABLE backfill_queue RENAME TO backfill_queue_old; -ALTER TABLE backfill_state RENAME TO backfill_state_old; -ALTER TABLE disappearing_message RENAME TO disappearing_message_old; -ALTER TABLE history_sync_message RENAME TO history_sync_message_old; -ALTER TABLE history_sync_conversation RENAME TO history_sync_conversation_old; -ALTER TABLE media_backfill_requests RENAME TO media_backfill_requests_old; -ALTER TABLE poll_option_id RENAME TO poll_option_id_old; -ALTER TABLE user_portal RENAME TO user_portal_old; -ALTER TABLE portal RENAME TO portal_old; -ALTER TABLE puppet RENAME TO puppet_old; -ALTER TABLE message RENAME TO message_old; -ALTER TABLE reaction RENAME TO reaction_old; -ALTER TABLE "user" RENAME TO user_old; -` - -//go:embed legacymigrate.sql -var legacyMigrateCopyData string - -func init() { - litestream.Functions["split_part"] = func(input, delimiter string, partNum int) string { - // split_part is 1-indexed - partNum-- - parts := strings.Split(input, delimiter) - if len(parts) <= partNum { - return "" - } - return parts[partNum] - } -} - -func migrateLegacyConfig(helper up.Helper) { - helper.Set(up.Str, "maunium.net/go/mautrix-whatsapp", "encryption", "pickle_key") - bridgeconfig.CopyToOtherLocation(helper, up.Str, []string{"whatsapp", "os_name"}, []string{"network", "os_name"}) - bridgeconfig.CopyToOtherLocation(helper, up.Str, []string{"whatsapp", "browser_name"}, []string{"network", "browser_name"}) - bridgeconfig.CopyToOtherLocation(helper, up.Str, []string{"whatsapp", "proxy"}, []string{"network", "proxy"}) - bridgeconfig.CopyToOtherLocation(helper, up.Str, []string{"whatsapp", "get_proxy_url"}, []string{"network", "get_proxy_url"}) - bridgeconfig.CopyToOtherLocation(helper, up.Bool, []string{"whatsapp", "proxy_only_login"}, []string{"network", "proxy_only_login"}) - bridgeconfig.CopyToOtherLocation(helper, up.Str, []string{"bridge", "displayname_template"}, []string{"network", "displayname_template"}) - bridgeconfig.CopyToOtherLocation(helper, up.Bool, []string{"bridge", "call_start_notices"}, []string{"network", "call_start_notices"}) - bridgeconfig.CopyToOtherLocation(helper, up.Bool, []string{"bridge", "send_presence_on_typing"}, []string{"network", "send_presence_on_typing"}) - bridgeconfig.CopyToOtherLocation(helper, up.Bool, []string{"bridge", "enable_status_broadcast"}, []string{"network", "enable_status_broadcast"}) - bridgeconfig.CopyToOtherLocation(helper, up.Bool, []string{"bridge", "disable_status_broadcast_send"}, []string{"network", "disable_status_broadcast_send"}) - bridgeconfig.CopyToOtherLocation(helper, up.Bool, []string{"bridge", "mute_status_broadcast"}, []string{"network", "mute_status_broadcast"}) - bridgeconfig.CopyToOtherLocation(helper, up.Str, []string{"bridge", "status_broadcast_tag"}, []string{"network", "status_broadcast_tag"}) - bridgeconfig.CopyToOtherLocation(helper, up.Bool, []string{"bridge", "whatsapp_thumbnail"}, []string{"network", "whatsapp_thumbnail"}) - bridgeconfig.CopyToOtherLocation(helper, up.Bool, []string{"bridge", "url_previews"}, []string{"network", "url_previews"}) - bridgeconfig.CopyToOtherLocation(helper, up.Bool, []string{"bridge", "extev_polls"}, []string{"network", "extev_polls"}) - bridgeconfig.CopyToOtherLocation(helper, up.Bool, []string{"bridge", "force_active_delivery_receipts"}, []string{"network", "force_active_delivery_receipts"}) - bridgeconfig.CopyToOtherLocation(helper, up.Int, []string{"bridge", "history_sync", "max_initial_conversations"}, []string{"network", "history_sync", "max_initial_conversations"}) - bridgeconfig.CopyToOtherLocation(helper, up.Bool, []string{"bridge", "history_sync", "request_full_sync"}, []string{"network", "history_sync", "request_full_sync"}) - bridgeconfig.CopyToOtherLocation(helper, up.Int, []string{"bridge", "history_sync", "full_sync_config", "days_limit"}, []string{"network", "history_sync", "full_sync_config", "days_limit"}) - bridgeconfig.CopyToOtherLocation(helper, up.Int, []string{"bridge", "history_sync", "full_sync_config", "size_limit_mb"}, []string{"network", "history_sync", "full_sync_config", "size_limit_mb"}) - bridgeconfig.CopyToOtherLocation(helper, up.Int, []string{"bridge", "history_sync", "full_sync_config", "storage_quota_mb"}, []string{"network", "history_sync", "full_sync_config", "storage_quota_mb"}) - bridgeconfig.CopyToOtherLocation(helper, up.Bool, []string{"bridge", "history_sync", "media_requests", "auto_request_media"}, []string{"network", "history_sync", "media_requests", "auto_request_media"}) - bridgeconfig.CopyToOtherLocation(helper, up.Str, []string{"bridge", "history_sync", "media_requests", "request_method"}, []string{"network", "history_sync", "media_requests", "request_method"}) - bridgeconfig.CopyToOtherLocation(helper, up.Int, []string{"bridge", "history_sync", "media_requests", "request_local_time"}, []string{"network", "history_sync", "media_requests", "request_local_time"}) - bridgeconfig.CopyToOtherLocation(helper, up.Int, []string{"bridge", "history_sync", "media_requests", "max_async_handle"}, []string{"network", "history_sync", "media_requests", "max_async_handle"}) -} diff --git a/cmd/mautrix-whatsapp/legacymigrate.sql b/cmd/mautrix-whatsapp/legacymigrate.sql deleted file mode 100644 index e3b9c3e..0000000 --- a/cmd/mautrix-whatsapp/legacymigrate.sql +++ /dev/null @@ -1,215 +0,0 @@ -INSERT INTO "user" (bridge_id, mxid, management_room, access_token) -SELECT '', mxid, management_room, '' -FROM user_old; - -UPDATE "user" SET access_token=COALESCE((SELECT access_token FROM puppet_old WHERE custom_mxid="user".mxid AND access_token<>'' LIMIT 1), ''); - -INSERT INTO user_login (bridge_id, user_mxid, id, remote_name, space_room, metadata, remote_profile) -SELECT - '', -- bridge_id - mxid, -- user_mxid - username, -- id - '+' || username, -- remote_name - space_room, - -- only: postgres - jsonb_build_object --- only: sqlite (line commented) --- json_object - ( - 'wa_device_id', device, - 'phone_last_seen', phone_last_seen, - 'phone_last_pinged', phone_last_pinged, - 'timezone', timezone - ), -- metadata - '{}' -- remote_profile -FROM user_old -WHERE username<>'' AND device<>0; - -INSERT INTO ghost ( - bridge_id, id, name, avatar_id, avatar_hash, avatar_mxc, - name_set, avatar_set, contact_info_set, is_bot, identifiers, metadata -) -SELECT - '', -- bridge_id - username, -- id - COALESCE(displayname, ''), -- name - COALESCE(avatar, ''), -- avatar_id - '', -- avatar_hash - COALESCE(avatar_url, ''), -- avatar_mxc - name_set, - avatar_set, - contact_info_set, - false, -- is_bot - '[]', -- identifiers - -- only: postgres - jsonb_build_object - -- only: sqlite (line commented) --- json_object - ( - 'last_sync', last_sync - -- TODO name quality - ) -- metadata -FROM puppet_old; - --- Some messages don't have senders, so insert an empty ghost to match the foreign key constraint. -INSERT INTO ghost (bridge_id, id, name, avatar_id, avatar_hash, avatar_mxc, name_set, avatar_set, contact_info_set, is_bot, identifiers, metadata) -VALUES ('', '', '', '', '', '', false, false, false, false, '[]', '{}') -ON CONFLICT (bridge_id, id) DO NOTHING; - -DELETE FROM portal_old WHERE jid LIKE '%@s.whatsapp.net' AND (receiver='' OR receiver IS NULL) and mxid IS NULL; - -INSERT INTO portal ( - bridge_id, id, receiver, mxid, parent_id, parent_receiver, relay_bridge_id, relay_login_id, other_user_id, - name, topic, avatar_id, avatar_hash, avatar_mxc, name_set, avatar_set, topic_set, - name_is_custom, in_space, room_type, disappear_type, disappear_timer, metadata -) -SELECT - '', -- bridge_id - jid, -- id - CASE WHEN receiver LIKE '%@s.whatsapp.net' THEN replace(receiver, '@s.whatsapp.net', '') ELSE '' END, -- receiver - mxid, - CASE WHEN EXISTS(SELECT 1 FROM portal_old WHERE jid=parent_group) THEN parent_group ELSE NULL END, -- parent_id - '', -- parent_receiver - CASE WHEN relay_user_id<>'' THEN '' END, -- relay_bridge_id - (SELECT id FROM user_login WHERE user_mxid=relay_user_id), -- relay_login_id - CASE WHEN jid LIKE '%@s.whatsapp.net' THEN replace(jid, '@s.whatsapp.net', '') ELSE '' END, -- other_user_id - name, - topic, - avatar, -- avatar_id - '', -- avatar_hash - COALESCE(avatar_url, ''), -- avatar_mxc - name_set, - avatar_set, - topic_set, - jid NOT LIKE '%@s.whatsapp.net', -- name_is_custom - in_space, - CASE - WHEN is_parent THEN 'space' - WHEN jid LIKE '%@s.whatsapp.net' THEN 'dm' - ELSE '' - END, -- room_type - CASE WHEN expiration_time>0 THEN 'after_read' END, -- disappear_type - CASE WHEN expiration_time > 0 THEN expiration_time * 1000000000 END, -- disappear_timer - -- only: postgres - jsonb_build_object - -- only: sqlite (line commented) --- json_object - ( - 'last_sync', last_sync - ) -- metadata -FROM portal_old; - --- only: sqlite -DELETE FROM user_portal_old WHERE rowid IN (SELECT rowid FROM pragma_foreign_key_check('user_portal_old')); - -INSERT INTO user_portal (bridge_id, user_mxid, login_id, portal_id, portal_receiver, in_space, preferred, last_read) -SELECT - '', -- bridge_id - user_mxid, - (SELECT id FROM user_login WHERE user_login.user_mxid=user_portal_old.user_mxid), -- login_id - portal_jid, -- portal_id - CASE WHEN portal_receiver LIKE '%@s.whatsapp.net' THEN replace(portal_receiver, '@s.whatsapp.net', '') ELSE '' END, -- portal_receiver - in_space, - false, -- preferred - last_read_ts * 1000000000 -- last_read -FROM user_portal_old WHERE EXISTS(SELECT 1 FROM user_login WHERE user_login.user_mxid=user_portal_old.user_mxid); - -ALTER TABLE message_old ADD COLUMN combined_id TEXT; -DELETE FROM message_old WHERE sender IS NULL; -UPDATE message_old SET combined_id = chat_jid || ':' || ( - CASE WHEN sender LIKE '%:%@s.whatsapp.net' - THEN (split_part(replace(sender, '@s.whatsapp.net', ''), ':', 1) || '@s.whatsapp.net') - ELSE sender - END -) || ':' || jid; -DELETE FROM message_old WHERE timestamp<0; --- only: sqlite for next 2 lines -DELETE FROM message_old WHERE rowid IN (SELECT rowid FROM pragma_foreign_key_check('message_old')); -DELETE FROM reaction_old WHERE rowid IN (SELECT rowid FROM pragma_foreign_key_check('reaction_old')); -DELETE FROM message_old WHERE sender NOT LIKE '%@s.whatsapp.net' AND sender<>chat_jid; -DELETE FROM reaction_old WHERE sender NOT LIKE '%@s.whatsapp.net'; -DELETE FROM reaction_old WHERE NOT EXISTS(SELECT 1 FROM puppet_old WHERE username=replace(sender, '@s.whatsapp.net', '')); - -INSERT INTO message ( - bridge_id, id, part_id, mxid, room_id, room_receiver, sender_id, sender_mxid, timestamp, edit_count, metadata -) -SELECT - '', -- bridge_id - combined_id, -- id - '', -- part_id - mxid, - chat_jid, -- room_id - CASE WHEN chat_receiver LIKE '%@s.whatsapp.net' THEN replace(chat_receiver, '@s.whatsapp.net', '') ELSE '' END, -- room_receiver - CASE WHEN sender=chat_jid AND sender NOT LIKE '%@s.whatsapp.net' - THEN '' - ELSE split_part(split_part(replace(sender, '@s.whatsapp.net', ''), ':', 1), '.', 1) - END, -- sender_id - sender_mxid, -- sender_mxid - timestamp * 1000000000, -- timestamp - 0, -- edit_count - -- only: postgres - jsonb_build_object - -- only: sqlite (line commented) --- json_object - ( - 'sender_device_id', CAST(nullif(split_part(replace(sender, '@s.whatsapp.net', ''), ':', 2), '') AS INTEGER), - 'broadcast_list_jid', broadcast_list_jid, - 'error', CAST(error AS TEXT) - ) -- metadata -FROM message_old; - -INSERT INTO reaction ( - bridge_id, message_id, message_part_id, sender_id, emoji_id, room_id, room_receiver, mxid, timestamp, emoji, metadata -) -SELECT - '', -- bridge_id - message_old.combined_id, -- message_id - '', -- message_part_id - replace(reaction_old.sender, '@s.whatsapp.net', ''), -- sender_id - '', -- emoji_id - reaction_old.chat_jid, -- room_id - CASE WHEN reaction_old.chat_receiver LIKE '%@s.whatsapp.net' THEN replace(reaction_old.chat_receiver, '@s.whatsapp.net', '') ELSE '' END, -- room_receiver - reaction_old.mxid, - 0, -- timestamp - '', -- emoji - -- only: postgres - jsonb_build_object - -- only: sqlite (line commented) --- json_object - ( - 'sender_device_id', CAST(nullif(split_part(replace(reaction_old.sender, '@s.whatsapp.net', ''), ':', 2), '') AS INTEGER) - ) -- metadata -FROM reaction_old -LEFT JOIN message_old - ON reaction_old.chat_jid = message_old.chat_jid - AND reaction_old.chat_receiver = message_old.chat_receiver - AND reaction_old.target_jid = message_old.jid; - -INSERT INTO disappearing_message (bridge_id, mx_room, mxid, type, timer, disappear_at) -SELECT - '', -- bridge_id - room_id, - event_id, - 'after_read', - expire_in * 1000000, -- timer - expire_at * 1000000 -- disappear_at -FROM disappearing_message_old -WHERE room_id IN (SELECT mxid FROM portal WHERE mxid IS NOT NULL); - -INSERT INTO whatsapp_poll_option_id (bridge_id, msg_mxid, opt_id, opt_hash) -SELECT '', msg_mxid, opt_id, opt_hash -FROM poll_option_id_old; - -DROP TABLE backfill_queue_old; -DROP TABLE backfill_state_old; -DROP TABLE disappearing_message_old; -DROP TABLE history_sync_message_old; -DROP TABLE history_sync_conversation_old; -DROP TABLE media_backfill_requests_old; -DROP TABLE poll_option_id_old; -DROP TABLE user_portal_old; -DROP TABLE reaction_old; -DROP TABLE message_old; -DROP TABLE puppet_old; -DROP TABLE portal_old; -DROP TABLE user_old; diff --git a/cmd/mautrix-whatsapp/legacyprovision.go b/cmd/mautrix-whatsapp/legacyprovision.go deleted file mode 100644 index 169fa01..0000000 --- a/cmd/mautrix-whatsapp/legacyprovision.go +++ /dev/null @@ -1,307 +0,0 @@ -package main - -import ( - "context" - "errors" - "net/http" - "regexp" - "strings" - - "github.com/gorilla/websocket" - "github.com/rs/zerolog/hlog" - "go.mau.fi/util/exhttp" - "go.mau.fi/whatsmeow/types" - "maunium.net/go/mautrix" - "maunium.net/go/mautrix/bridgev2" - "maunium.net/go/mautrix/bridgev2/matrix" - "maunium.net/go/mautrix/bridgev2/status" - "maunium.net/go/mautrix/id" - - "go.mau.fi/mautrix-whatsapp/pkg/connector" - "go.mau.fi/mautrix-whatsapp/pkg/waid" -) - -var upgrader = websocket.Upgrader{ - CheckOrigin: func(r *http.Request) bool { - return true - }, - Subprotocols: []string{"net.maunium.whatsapp.login"}, -} - -func legacyProvAuth(r *http.Request) string { - if !strings.HasSuffix(r.URL.Path, "/v1/login") { - return "" - } - authParts := strings.Split(r.Header.Get("Sec-WebSocket-Protocol"), ",") - for _, part := range authParts { - part = strings.TrimSpace(part) - if strings.HasPrefix(part, "net.maunium.whatsapp.auth-") { - return strings.TrimPrefix(part, "net.maunium.whatsapp.auth-") - } - } - return "" -} - -type OtherUserInfo struct { - MXID id.UserID `json:"mxid"` - JID types.JID `json:"jid"` - Name string `json:"displayname"` - Avatar id.ContentURIString `json:"avatar_url"` -} - -type PortalInfo struct { - RoomID id.RoomID `json:"room_id"` - OtherUser *OtherUserInfo `json:"other_user,omitempty"` - GroupInfo *types.GroupInfo `json:"group_info,omitempty"` - JustCreated bool `json:"just_created"` -} - -type Error struct { - Success bool `json:"success"` - Error string `json:"error"` - ErrCode string `json:"errcode"` -} - -type Response struct { - Success bool `json:"success"` - Status string `json:"status"` -} - -func respondWebsocketWithError(conn *websocket.Conn, err error, message string) { - var mautrixRespErr mautrix.RespError - var bv2RespErr bridgev2.RespError - if errors.As(err, &bv2RespErr) { - mautrixRespErr = mautrix.RespError(bv2RespErr) - } else if !errors.As(err, &mautrixRespErr) { - mautrixRespErr = mautrix.RespError{ - Err: message, - ErrCode: "M_UNKNOWN", - StatusCode: http.StatusInternalServerError, - } - } - _ = conn.WriteJSON(&mautrixRespErr) -} - -var notNumbers = regexp.MustCompile("[^0-9]") - -func legacyProvLogin(w http.ResponseWriter, r *http.Request) { - log := hlog.FromRequest(r) - - conn, err := upgrader.Upgrade(w, r, nil) - if err != nil { - log.Err(err).Msg("Failed to upgrade connection to websocket") - return - } - defer func() { - err := conn.Close() - if err != nil { - log.Debug().Err(err).Msg("Error closing websocket") - } - }() - - go func() { - // Read everything so SetCloseHandler() works - for { - _, _, err = conn.ReadMessage() - if err != nil { - break - } - } - }() - ctx, cancel := context.WithCancel(context.Background()) - conn.SetCloseHandler(func(code int, text string) error { - log.Debug().Int("close_code", code).Msg("Login websocket closed, cancelling login") - cancel() - return nil - }) - - user := m.Matrix.Provisioning.GetUser(r) - loginFlowID := connector.LoginFlowIDQR - phoneNum := r.URL.Query().Get("phone_number") - if phoneNum != "" { - phoneNum = notNumbers.ReplaceAllString(phoneNum, "") - if len(phoneNum) < 7 || strings.HasPrefix(phoneNum, "0") { - errorMsg := "Invalid phone number" - if len(phoneNum) > 6 { - errorMsg = "Please enter the phone number in international format" - } - _ = conn.WriteJSON(Error{ - Error: errorMsg, - ErrCode: "invalid phone number", - }) - return - } - loginFlowID = connector.LoginFlowIDPhone - } - login, err := c.CreateLogin(ctx, user, loginFlowID) - if err != nil { - log.Err(err).Msg("Failed to create login") - respondWebsocketWithError(conn, err, "Failed to create login") - return - } - waLogin := login.(*connector.WALogin) - waLogin.Timezone = r.URL.Query().Get("tz") - step, err := waLogin.Start(ctx) - if err != nil { - log.Err(err).Msg("Failed to start login") - respondWebsocketWithError(conn, err, "Failed to start login") - return - } - if phoneNum != "" { - if step.StepID != connector.LoginStepIDPhoneNumber { - respondWebsocketWithError(conn, errors.New("unexpected step"), "Unexpected step while starting phone number login") - waLogin.Cancel() - return - } - step, err = waLogin.SubmitUserInput(ctx, map[string]string{"phone_number": phoneNum}) - if err != nil { - log.Err(err).Msg("Failed to submit phone number") - respondWebsocketWithError(conn, err, "Failed to start phone code login") - return - } else if step.StepID != connector.LoginStepIDCode { - respondWebsocketWithError(conn, errors.New("unexpected step"), "Unexpected step after submitting phone number") - waLogin.Cancel() - return - } - _ = conn.WriteJSON(map[string]any{ - "pairing_code": step.DisplayAndWaitParams.Data, - "timeout": 180, - }) - } else if step.StepID != connector.LoginStepIDQR { - respondWebsocketWithError(conn, errors.New("unexpected step"), "Unexpected step while starting QR login") - waLogin.Cancel() - return - } else { - _ = conn.WriteJSON(map[string]any{ - "code": step.DisplayAndWaitParams.Data, - "timeout": 60, - }) - } - for { - step, err = waLogin.Wait(ctx) - if err != nil { - log.Err(err).Msg("Failed to wait for login") - respondWebsocketWithError(conn, err, "Failed to wait for login") - } else if step.StepID == connector.LoginStepIDQR { - _ = conn.WriteJSON(map[string]any{ - "code": step.DisplayAndWaitParams.Data, - "timeout": 20, - }) - continue - } else if step.StepID != connector.LoginStepIDComplete { - respondWebsocketWithError(conn, errors.New("unexpected step"), "Unexpected step while waiting for login") - waLogin.Cancel() - } else { - // TODO delete old logins - _ = conn.WriteJSON(map[string]any{ - "success": true, - "jid": waid.ParseUserLoginID(step.CompleteParams.UserLoginID, step.CompleteParams.UserLogin.Metadata.(*waid.UserLoginMetadata).WADeviceID).String(), - "platform": step.CompleteParams.UserLogin.Client.(*connector.WhatsAppClient).Device.Platform, - "phone": step.CompleteParams.UserLogin.RemoteProfile.Phone, - }) - go handleLoginComplete(context.WithoutCancel(ctx), user, step.CompleteParams.UserLogin) - } - break - } -} -func handleLoginComplete(ctx context.Context, user *bridgev2.User, newLogin *bridgev2.UserLogin) { - allLogins := user.GetUserLogins() - for _, login := range allLogins { - if login.ID != newLogin.ID { - login.Delete(ctx, status.BridgeState{StateEvent: status.StateLoggedOut, Reason: "LOGIN_OVERRIDDEN"}, bridgev2.DeleteOpts{}) - } - } -} - -func legacyProvLogout(w http.ResponseWriter, r *http.Request) { - user := m.Matrix.Provisioning.GetUser(r) - allLogins := user.GetUserLogins() - if len(allLogins) == 0 { - exhttp.WriteJSONResponse(w, http.StatusOK, Error{ - Error: "You're not logged in", - ErrCode: "not logged in", - }) - return - } - for _, login := range allLogins { - // Intentionally don't delete the user login, only logout remote - login.Client.(*connector.WhatsAppClient).LogoutRemote(r.Context()) - } - exhttp.WriteJSONResponse(w, http.StatusOK, Response{true, "Logged out successfully"}) -} - -func legacyProvContacts(w http.ResponseWriter, r *http.Request) { - userLogin := m.Matrix.Provisioning.GetLoginForRequest(w, r) - if userLogin == nil { - return - } - if contacts, err := userLogin.Client.(*connector.WhatsAppClient).Device.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", - ErrCode: "failed to get contacts", - }) - } else { - augmentedContacts := map[types.JID]any{} - for jid, contact := range contacts { - var avatarURL id.ContentURIString - if puppet, _ := m.Bridge.GetExistingGhostByID(r.Context(), waid.MakeUserID(jid)); puppet != nil { - avatarURL = puppet.AvatarMXC - } - augmentedContacts[jid] = map[string]interface{}{ - "Found": contact.Found, - "FirstName": contact.FirstName, - "FullName": contact.FullName, - "PushName": contact.PushName, - "BusinessName": contact.BusinessName, - "AvatarURL": avatarURL, - } - } - exhttp.WriteJSONResponse(w, http.StatusOK, augmentedContacts) - } -} - -func legacyProvResolveIdentifier(w http.ResponseWriter, r *http.Request) { - number := r.PathValue("number") - userLogin := m.Matrix.Provisioning.GetLoginForRequest(w, r) - if userLogin == nil { - return - } - startChat := strings.Contains(r.URL.Path, "/v1/pm/") - resp, err := userLogin.Client.(*connector.WhatsAppClient).ResolveIdentifier(r.Context(), number, startChat) - if err != nil { - hlog.FromRequest(r).Warn().Err(err).Str("identifier", number).Msg("Failed to resolve identifier") - matrix.RespondWithError(w, err, "Internal error resolving identifier") - return - } - var portal *bridgev2.Portal - if startChat { - portal, err = m.Bridge.GetPortalByKey(r.Context(), resp.Chat.PortalKey) - if err != nil { - hlog.FromRequest(r).Warn().Err(err).Stringer("portal_key", resp.Chat.PortalKey).Msg("Failed to get portal by key") - matrix.RespondWithError(w, err, "Internal error getting portal by key") - return - } - err = portal.CreateMatrixRoom(r.Context(), userLogin, nil) - if err != nil { - hlog.FromRequest(r).Warn().Err(err).Stringer("portal_key", resp.Chat.PortalKey).Msg("Failed to create matrix room for portal") - matrix.RespondWithError(w, err, "Internal error creating matrix room for portal") - return - } - } else { - portal, _ = m.Bridge.GetExistingPortalByKey(r.Context(), resp.Chat.PortalKey) - } - var roomID id.RoomID - if portal != nil { - roomID = portal.MXID - } - exhttp.WriteJSONResponse(w, http.StatusOK, PortalInfo{ - RoomID: roomID, - OtherUser: &OtherUserInfo{ - JID: waid.ParseUserID(resp.UserID), - MXID: resp.Ghost.Intent.GetMXID(), - Name: resp.Ghost.Name, - Avatar: resp.Ghost.AvatarMXC, - }, - }) -} diff --git a/cmd/mautrix-whatsapp/main.go b/cmd/mautrix-whatsapp/main.go index 33dc865..9dbf088 100644 --- a/cmd/mautrix-whatsapp/main.go +++ b/cmd/mautrix-whatsapp/main.go @@ -1,11 +1,9 @@ package main import ( - "maunium.net/go/mautrix/bridgev2/bridgeconfig" "maunium.net/go/mautrix/bridgev2/matrix/mxmain" "go.mau.fi/mautrix-whatsapp/pkg/connector" - "go.mau.fi/mautrix-whatsapp/pkg/connector/wadb/upgrades" ) // Information to find out exactly which commit the bridge was built from. @@ -16,39 +14,15 @@ var ( BuildTime = "unknown" ) -var c = &connector.WhatsAppConnector{} var m = mxmain.BridgeMain{ Name: "mautrix-whatsapp", URL: "https://github.com/mautrix/whatsapp", Description: "A Matrix-WhatsApp puppeting bridge.", Version: "0.12.4", - Connector: c, + Connector: &connector.WhatsAppConnector{}, } func main() { - bridgeconfig.HackyMigrateLegacyNetworkConfig = migrateLegacyConfig - m.PostInit = func() { - m.CheckLegacyDB( - 57, - "v0.8.6", - "v0.11.0", - m.LegacyMigrateWithAnotherUpgrader( - legacyMigrateRenameTables, legacyMigrateCopyData, 21, - upgrades.Table, "whatsapp_version", 5, - ), - true, - ) - } - m.PostStart = func() { - if m.Matrix.Provisioning != nil { - m.Matrix.Provisioning.Router.HandleFunc("GET /v1/login", legacyProvLogin) - m.Matrix.Provisioning.Router.HandleFunc("POST /v1/logout", legacyProvLogout) - 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.GetAuthFromRequest = legacyProvAuth - } - } m.InitVersion(Tag, Commit, BuildTime) m.Run() } diff --git a/go.mod b/go.mod index 3ffe06e..c0c6f67 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,6 @@ go 1.24.0 toolchain go1.25.0 require ( - github.com/gorilla/websocket v1.5.0 github.com/lib/pq v1.10.9 github.com/rs/zerolog v1.34.0 go.mau.fi/util v0.9.0 @@ -16,7 +15,7 @@ require ( golang.org/x/sync v0.16.0 google.golang.org/protobuf v1.36.7 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.25.1-0.20250826140716-0345a5356de1 + maunium.net/go/mautrix v0.25.1-0.20250827232041-3048d2edab7a ) require ( @@ -24,6 +23,7 @@ require ( github.com/coder/websocket v1.8.13 // indirect github.com/coreos/go-systemd/v22 v22.5.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 e591eb1..e5f42c2 100644 --- a/go.sum +++ b/go.sum @@ -101,5 +101,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.1-0.20250826140716-0345a5356de1 h1:8U8dwv4dxhMLrtqjf9LoDuQ6daGj9qdw1/62HIvK2cY= -maunium.net/go/mautrix v0.25.1-0.20250826140716-0345a5356de1/go.mod h1:pDd6Ppg+1PbWrw/rg4ZQQfVYZICRGzH+DcliZ/BODvU= +maunium.net/go/mautrix v0.25.1-0.20250827232041-3048d2edab7a h1:dUczO0BSW59ldg2UC5ZhybfEnpGwLxMr7JPnKGt9+kE= +maunium.net/go/mautrix v0.25.1-0.20250827232041-3048d2edab7a/go.mod h1:pDd6Ppg+1PbWrw/rg4ZQQfVYZICRGzH+DcliZ/BODvU= From bd0d24e1e44f098c72c3f966638a1679a1623a85 Mon Sep 17 00:00:00 2001 From: Peter Smit <43498358+Petersmit27@users.noreply.github.com> Date: Thu, 28 Aug 2025 02:08:25 +0200 Subject: [PATCH 112/276] handlematrix: add support for room name/topic/avatar changes (#834) Fixes #833 Co-authored-by: Tulir Asokan --- pkg/connector/chatinfo.go | 13 ++++ pkg/connector/handlematrix.go | 143 ++++++++++++++++++++++++++++++++++ pkg/waid/dbmeta.go | 1 + 3 files changed, 157 insertions(+) diff --git a/pkg/connector/chatinfo.go b/pkg/connector/chatinfo.go index 1a912a0..58855b5 100644 --- a/pkg/connector/chatinfo.go +++ b/pkg/connector/chatinfo.go @@ -225,6 +225,17 @@ func setAddressingMode(mode types.AddressingMode) bridgev2.ExtraUpdater[*bridgev } } +func setTopicID(id, topic string) bridgev2.ExtraUpdater[*bridgev2.Portal] { + return func(_ context.Context, portal *bridgev2.Portal) bool { + meta := portal.Metadata.(*waid.PortalMetadata) + if meta.TopicID != id && portal.Topic == topic { + meta.TopicID = id + return true + } + return false + } +} + func (wa *WhatsAppClient) wrapGroupInfo(ctx context.Context, info *types.GroupInfo) *bridgev2.ChatInfo { sendEventPL := defaultPL if info.IsAnnounce && !info.IsDefaultSubGroup { @@ -238,6 +249,7 @@ func (wa *WhatsAppClient) wrapGroupInfo(ctx context.Context, info *types.GroupIn wa.makePortalAvatarFetcher("", types.EmptyJID, time.Time{}), setDefaultSubGroupFlag(info.IsDefaultSubGroup && info.IsAnnounce), setAddressingMode(info.AddressingMode), + setTopicID(info.TopicID, info.Topic), ) wrapped := &bridgev2.ChatInfo{ Name: ptr.Ptr(info.Name), @@ -317,6 +329,7 @@ func (wa *WhatsAppClient) wrapGroupInfoChange(ctx context.Context, evt *events.G } if evt.Topic != nil { changes.Topic = &evt.Topic.Topic + changes.ExtraUpdates = bridgev2.MergeExtraUpdaters(changes.ExtraUpdates, setTopicID(evt.Topic.TopicID, evt.Topic.Topic)) } if evt.Ephemeral != nil { changes.Disappear = &database.DisappearingSetting{ diff --git a/pkg/connector/handlematrix.go b/pkg/connector/handlematrix.go index d271da6..c12ca98 100644 --- a/pkg/connector/handlematrix.go +++ b/pkg/connector/handlematrix.go @@ -1,9 +1,13 @@ package connector import ( + "bytes" "context" + "crypto/sha256" "errors" "fmt" + "image" + "image/jpeg" "strings" "time" @@ -13,6 +17,7 @@ import ( "go.mau.fi/whatsmeow" "go.mau.fi/whatsmeow/proto/waE2E" "go.mau.fi/whatsmeow/types" + "golang.org/x/image/draw" "google.golang.org/protobuf/proto" "maunium.net/go/mautrix/bridgev2" "maunium.net/go/mautrix/bridgev2/database" @@ -32,6 +37,9 @@ var ( _ bridgev2.PollHandlingNetworkAPI = (*WhatsAppClient)(nil) _ bridgev2.DisappearTimerChangingNetworkAPI = (*WhatsAppClient)(nil) _ bridgev2.MembershipHandlingNetworkAPI = (*WhatsAppClient)(nil) + _ bridgev2.RoomNameHandlingNetworkAPI = (*WhatsAppClient)(nil) + _ bridgev2.RoomTopicHandlingNetworkAPI = (*WhatsAppClient)(nil) + _ bridgev2.RoomAvatarHandlingNetworkAPI = (*WhatsAppClient)(nil) ) func (wa *WhatsAppClient) HandleMatrixPollStart(ctx context.Context, msg *bridgev2.MatrixPollStart) (*bridgev2.MatrixMessageResponse, error) { @@ -423,3 +431,138 @@ func (wa *WhatsAppClient) HandleMatrixMembership(ctx context.Context, msg *bridg return true, nil } + +func (wa *WhatsAppClient) HandleMatrixRoomName(ctx context.Context, msg *bridgev2.MatrixRoomName) (bool, error) { + portalJID, err := waid.ParsePortalID(msg.Portal.ID) + if err != nil { + return false, err + } + + if msg.Portal.RoomType == database.RoomTypeDM { + return false, fmt.Errorf("cannot set room name for DM") + } + + err = wa.Client.SetGroupName(portalJID, msg.Content.Name) + if err != nil { + return false, err + } + + msg.Portal.Name = msg.Content.Name + msg.Portal.NameSet = true + + return true, nil +} + +func (wa *WhatsAppClient) HandleMatrixRoomTopic(ctx context.Context, msg *bridgev2.MatrixRoomTopic) (bool, error) { + portalJID, err := waid.ParsePortalID(msg.Portal.ID) + if err != nil { + return false, err + } + + if msg.Portal.RoomType == database.RoomTypeDM { + return false, fmt.Errorf("cannot set room topic for DM") + } + + newID := wa.Client.GenerateMessageID() + oldID := msg.Portal.Metadata.(*waid.PortalMetadata).TopicID + err = wa.Client.SetGroupTopic(portalJID, oldID, newID, msg.Content.Topic) + if err != nil { + return false, err + } + + msg.Portal.Topic = msg.Content.Topic + msg.Portal.TopicSet = true + msg.Portal.Metadata.(*waid.PortalMetadata).TopicID = newID + + return true, nil +} + +func (wa *WhatsAppClient) HandleMatrixRoomAvatar(ctx context.Context, msg *bridgev2.MatrixRoomAvatar) (bool, error) { + portalJID, err := waid.ParsePortalID(msg.Portal.ID) + if err != nil { + return false, err + } + + if msg.Portal.RoomType == database.RoomTypeDM { + return false, fmt.Errorf("cannot set room avatar for DM") + } + + 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 err != nil { + return false, fmt.Errorf("failed to download avatar: %w", err) + } + + data, err = convertRoomAvatar(data) + if err != nil { + return false, err + } + } + + avatarID, err := wa.Client.SetGroupPhoto(portalJID, data) + if err != nil { + return false, err + } + + msg.Portal.AvatarMXC = msg.Content.URL + if data == nil { + msg.Portal.AvatarHash = [32]byte{} + msg.Portal.AvatarID = "remove" + } else { + msg.Portal.AvatarHash = sha256.Sum256(data) + msg.Portal.AvatarID = networkid.AvatarID(avatarID) + } + msg.Portal.AvatarSet = true + + return true, nil +} + +const avatarMaxSize = 720 +const avatarMinSize = 190 + +func convertRoomAvatar(data []byte) ([]byte, error) { + cfg, imageType, err := image.DecodeConfig(bytes.NewReader(data)) + if err != nil { + return nil, fmt.Errorf("failed to decode avatar: %w", err) + } + width, height := cfg.Width, cfg.Height + isCorrectSize := width == height && avatarMinSize < width && width < avatarMaxSize + if isCorrectSize && imageType == "jpeg" { + return data, nil + } else if len(data) > 10*1024*1024 || width > 12000 || height > 12000 { + return nil, fmt.Errorf("avatar is too large for re-encoding") + } + + img, _, err := image.Decode(bytes.NewReader(data)) + if err != nil { + return nil, fmt.Errorf("failed to decode avatar: %w", err) + } + + if !isCorrectSize { + var squareCrop image.Rectangle + var dstSize int + if width > height { + dstSize = max(avatarMinSize, min(height, avatarMaxSize)) + + offset := (width - height) / 2 + squareCrop = image.Rect(offset, 0, width-offset, height) + } else { + dstSize = max(avatarMinSize, min(width, avatarMaxSize)) + + offset := (height - width) / 2 + squareCrop = image.Rect(0, offset, width, height-offset) + } + + cropped := image.NewRGBA(image.Rect(0, 0, dstSize, dstSize)) + draw.BiLinear.Scale(cropped, cropped.Rect, img, squareCrop, draw.Src, nil) + img = cropped + } + + var buf bytes.Buffer + err = jpeg.Encode(&buf, img, &jpeg.Options{Quality: jpeg.DefaultQuality}) + if err != nil { + return nil, fmt.Errorf("failed to re-encode avatar: %w", err) + } + return buf.Bytes(), nil +} diff --git a/pkg/waid/dbmeta.go b/pkg/waid/dbmeta.go index 58b1521..81fd895 100644 --- a/pkg/waid/dbmeta.go +++ b/pkg/waid/dbmeta.go @@ -106,6 +106,7 @@ type ReactionMetadata struct { type PortalMetadata struct { DisappearingTimerSetAt int64 `json:"disappearing_timer_set_at,omitempty"` + TopicID string `json:"topic_id,omitempty"` LastSync jsontime.Unix `json:"last_sync,omitempty"` CommunityAnnouncementGroup bool `json:"is_cag,omitempty"` AddressingMode types.AddressingMode `json:"addressing_mode,omitempty"` From 38390b9801af04e355e96ce0aa48ca2514b740b6 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 31 Aug 2025 22:25:00 +0300 Subject: [PATCH 113/276] changelog: document requirement to have an up-to-date config --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b85c5e..ef09e27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ * Removed legacy provisioning API and database legacy migration. Upgrading directly from versions prior to v0.11.0 is not supported. + * If you've been using the bridge since before v0.11.0 and have prevented the + bridge from writing to the config, you must either update the config + manually or allow the bridge to update it for you **before** upgrading to + this release. * Added support for `com.beeper.disappearing_timer` state event, which stores the disappearing setting of chats and allows changing the setting from Matrix. * Added lottieconverter to Docker images to enable converting animated stickers From 55d275b9e98893781ab05d9691ebb9ca30e272be Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 1 Sep 2025 18:03:59 +0300 Subject: [PATCH 114/276] startchat: add support for creating groups --- go.mod | 4 +- go.sum | 8 +- pkg/connector/capabilities.go | 35 +++++++-- pkg/connector/client.go | 2 + pkg/connector/handlewhatsapp.go | 3 + pkg/connector/startchat.go | 129 ++++++++++++++++++++++++++++++++ 6 files changed, 167 insertions(+), 14 deletions(-) diff --git a/go.mod b/go.mod index c0c6f67..b0f098e 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.0 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250825151841-7b1a3f7e5746 + go.mau.fi/whatsmeow v0.0.0-20250829123043-72d2ed58e998 golang.org/x/image v0.30.0 golang.org/x/net v0.43.0 golang.org/x/sync v0.16.0 google.golang.org/protobuf v1.36.7 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.25.1-0.20250827232041-3048d2edab7a + maunium.net/go/mautrix v0.25.1-0.20250901150120-f8c3a95de7a1 ) require ( diff --git a/go.sum b/go.sum index e5f42c2..773f393 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ go.mau.fi/util v0.9.0 h1:ya3s3pX+Y8R2fgp0DbE7a0o3FwncoelDX5iyaeVE8ls= go.mau.fi/util v0.9.0/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-20250825151841-7b1a3f7e5746 h1:WquxGXJBTlOTkBSltW52rqzRbQDAYD7JJgMli6FiIiY= -go.mau.fi/whatsmeow v0.0.0-20250825151841-7b1a3f7e5746/go.mod h1:xD0DR3s4T6PDd3BzgQG05AzLWxdKCmnvdCP3UuQvn9w= +go.mau.fi/whatsmeow v0.0.0-20250829123043-72d2ed58e998 h1:d6Utj53kD7W3O1actrw40Od7xC2/UUr/LHwbIkamE78= +go.mau.fi/whatsmeow v0.0.0-20250829123043-72d2ed58e998/go.mod h1:xD0DR3s4T6PDd3BzgQG05AzLWxdKCmnvdCP3UuQvn9w= 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= @@ -101,5 +101,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.1-0.20250827232041-3048d2edab7a h1:dUczO0BSW59ldg2UC5ZhybfEnpGwLxMr7JPnKGt9+kE= -maunium.net/go/mautrix v0.25.1-0.20250827232041-3048d2edab7a/go.mod h1:pDd6Ppg+1PbWrw/rg4ZQQfVYZICRGzH+DcliZ/BODvU= +maunium.net/go/mautrix v0.25.1-0.20250901150120-f8c3a95de7a1 h1:6eF+jMxhKxKADdJCHQ9SkmY8+xBpjObRfOOHwHgqrqg= +maunium.net/go/mautrix v0.25.1-0.20250901150120-f8c3a95de7a1/go.mod h1:pDd6Ppg+1PbWrw/rg4ZQQfVYZICRGzH+DcliZ/BODvU= diff --git a/pkg/connector/capabilities.go b/pkg/connector/capabilities.go index f850cf0..f9b1925 100644 --- a/pkg/connector/capabilities.go +++ b/pkg/connector/capabilities.go @@ -16,6 +16,32 @@ import ( var WhatsAppGeneralCaps = &bridgev2.NetworkGeneralCapabilities{ DisappearingMessages: true, AggressiveUpdateInfo: true, + Provisioning: bridgev2.ProvisioningCapabilities{ + ResolveIdentifier: bridgev2.ResolveIdentifierCapabilities{ + CreateDM: true, + LookupPhone: true, + ContactList: true, + }, + GroupCreation: map[string]bridgev2.GroupTypeCapabilities{ + "group": { + TypeDescription: "a group chat", + + Name: bridgev2.GroupFieldCapability{Allowed: true, MaxLength: 100}, + Disappear: bridgev2.GroupFieldCapability{Allowed: true, DisappearSettings: waDisappearingCap}, + Participants: bridgev2.GroupFieldCapability{Allowed: true, Required: true, MinLength: 1}, + Parent: bridgev2.GroupFieldCapability{Allowed: true}, + }, + }, + }, +} + +var waDisappearingCap = &event.DisappearingTimerCapability{ + Types: []event.DisappearingType{event.DisappearingTypeAfterSend}, + Timers: []jsontime.Milliseconds{ + jsontime.MS(24 * time.Hour), // 24 hours + jsontime.MS(7 * 24 * time.Hour), // 7 days + jsontime.MS(90 * 24 * time.Hour), // 90 days + }, } func (wa *WhatsAppConnector) GetCapabilities() *bridgev2.NetworkGeneralCapabilities { @@ -148,14 +174,7 @@ var whatsappCaps = &event.RoomFeatures{ ReactionCount: 1, ReadReceipts: true, TypingNotifications: true, - DisappearingTimer: &event.DisappearingTimerCapability{ - Types: []event.DisappearingType{event.DisappearingTypeAfterSend}, - Timers: []jsontime.Milliseconds{ - jsontime.MS(24 * time.Hour), // 24 hours - jsontime.MS(7 * 24 * time.Hour), // 7 days - jsontime.MS(90 * 24 * time.Hour), // 90 days - }, - }, + DisappearingTimer: waDisappearingCap, } var whatsappCAGCaps *event.RoomFeatures diff --git a/pkg/connector/client.go b/pkg/connector/client.go index a0951f8..aefa5c6 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -53,6 +53,7 @@ func (wa *WhatsAppConnector) LoadUserLogin(ctx context.Context, login *bridgev2. directMediaRetries: make(map[networkid.MessageID]*directMediaRetry), mediaRetryLock: semaphore.NewWeighted(wa.Config.HistorySync.MediaRequests.MaxAsyncHandle), pushNamesSynced: exsync.NewEvent(), + createDedup: exsync.NewSet[types.MessageID](), } login.Client = w @@ -116,6 +117,7 @@ type WhatsAppClient struct { isNewLogin bool pushNamesSynced *exsync.Event lastPresence types.Presence + createDedup *exsync.Set[types.MessageID] } var ( diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index 3e22f4e..e5e8029 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -670,6 +670,9 @@ func (wa *WhatsAppClient) handleWAGroupInfoChange(ctx context.Context, evt *even } func (wa *WhatsAppClient) handleWAJoinedGroup(ctx context.Context, evt *events.JoinedGroup) bool { + if wa.createDedup.Pop(evt.CreateKey) { + return true + } return wa.UserLogin.QueueRemoteEvent(&simplevent.ChatResync{ EventMeta: simplevent.EventMeta{ Type: bridgev2.RemoteEventChatResync, diff --git a/pkg/connector/startchat.go b/pkg/connector/startchat.go index 8543bfa..7e03fc0 100644 --- a/pkg/connector/startchat.go +++ b/pkg/connector/startchat.go @@ -18,14 +18,22 @@ package connector import ( "context" + "crypto/sha256" "errors" "fmt" "strings" + "time" + "github.com/rs/zerolog" + "go.mau.fi/util/ptr" + "go.mau.fi/whatsmeow" "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" + "maunium.net/go/mautrix/id" "go.mau.fi/mautrix-whatsapp/pkg/waid" ) @@ -35,6 +43,7 @@ var ( _ bridgev2.ContactListingNetworkAPI = (*WhatsAppClient)(nil) _ bridgev2.UserSearchingNetworkAPI = (*WhatsAppClient)(nil) _ bridgev2.GhostDMCreatingNetworkAPI = (*WhatsAppClient)(nil) + _ bridgev2.GroupCreatingNetworkAPI = (*WhatsAppClient)(nil) _ bridgev2.IdentifierValidatingNetwork = (*WhatsAppConnector)(nil) ) @@ -192,3 +201,123 @@ func (wa *WhatsAppClient) getContactList(ctx context.Context, filter string) ([] } return resp, nil } + +func (wa *WhatsAppClient) CreateGroup(ctx context.Context, params *bridgev2.GroupCreateParams) (*bridgev2.CreateChatResponse, error) { + createKey := wa.Client.GenerateMessageID() + if params.RoomID != "" { + wa.createDedup.Add(createKey) + } + req := whatsmeow.ReqCreateGroup{ + Name: ptr.Val(params.Name).Name, + Participants: make([]types.JID, len(params.Participants)), + CreateKey: createKey, + } + for i, participant := range params.Participants { + req.Participants[i] = waid.ParseUserID(participant) + } + if params.Parent != nil { + var err error + req.GroupLinkedParent.LinkedParentJID, err = waid.ParsePortalID(params.Parent.ID) + if err != nil { + return nil, fmt.Errorf("failed to parse parent ID: %w", err) + } + } + if params.Disappear != nil { + req.GroupEphemeral = types.GroupEphemeral{ + IsEphemeral: true, + DisappearingTimer: uint32(params.Disappear.Timer.Seconds()), + } + } + var avatarBytes []byte + var avatarMXC id.ContentURIString + if params.Avatar != nil { + avatarMXC = params.Avatar.URL + var err error + avatarBytes, err = wa.Main.Bridge.Bot.DownloadMedia(ctx, params.Avatar.URL, params.Avatar.MSC3414File) + if err != nil { + return nil, fmt.Errorf("failed to download avatar: %w", err) + } + avatarBytes, err = convertRoomAvatar(avatarBytes) + if err != nil { + return nil, err + } + } + resp, err := wa.Client.CreateGroup(ctx, req) + if err != nil { + return nil, fmt.Errorf("failed to create group: %w", err) + } + portal, err := wa.Main.Bridge.GetPortalByKey(ctx, wa.makeWAPortalKey(resp.JID)) + if err != nil { + return nil, fmt.Errorf("failed to get portal: %w", err) + } + groupInfo := wa.wrapGroupInfo(ctx, resp) + if params.RoomID != "" { + err = portal.UpdateMatrixRoomID(ctx, params.RoomID, bridgev2.UpdateMatrixRoomIDParams{ + SyncDBMetadata: func() { + portal.Name = req.Name + portal.NameSet = true + portal.ParentKey = ptr.Val(params.Parent) + if avatarBytes != nil { + portal.AvatarSet = true + portal.AvatarHash = sha256.Sum256(avatarBytes) + portal.AvatarMXC = avatarMXC + } + if req.DisappearingTimer > 0 { + portal.Disappear = database.DisappearingSetting{ + Type: event.DisappearingTypeAfterSend, + Timer: time.Duration(req.DisappearingTimer) * time.Second, + } + } + }, + OverwriteOldPortal: true, + TombstoneOldRoom: true, + DeleteOldRoom: true, + ChatInfo: groupInfo, + ChatInfoSource: wa.UserLogin, + }) + if err != nil { + return nil, fmt.Errorf("failed to update room ID after creating group: %w", err) + } + } + changed := false + if avatarBytes != nil { + avatarID, err := wa.Client.SetGroupPhoto(resp.JID, avatarBytes) + if err != nil { + zerolog.Ctx(ctx).Warn().Err(err).Msg("Failed to set group avatar after creating group") + } else { + portal.AvatarID = networkid.AvatarID(avatarID) + portal.AvatarHash = sha256.Sum256(avatarBytes) + portal.AvatarMXC = avatarMXC + groupInfo.Avatar = &bridgev2.Avatar{ + ID: portal.AvatarID, + MXC: portal.AvatarMXC, + Hash: portal.AvatarHash, + } + changed = true + } + } + if params.Topic != nil { + newTopicID := wa.Client.GenerateMessageID() + err = wa.Client.SetGroupTopic(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 { + portal.Topic = params.Topic.Topic + portal.TopicSet = params.RoomID != "" + portal.Metadata.(*waid.PortalMetadata).TopicID = newTopicID + changed = true + groupInfo.Topic = ¶ms.Topic.Topic + } + } + if changed { + err = portal.Save(ctx) + if err != nil { + zerolog.Ctx(ctx).Err(err).Msg("Failed to save portal after post-creation updates") + } + } + return &bridgev2.CreateChatResponse{ + PortalKey: wa.makeWAPortalKey(resp.JID), + Portal: portal, + PortalInfo: groupInfo, + }, nil +} From 3c96a4186c43b611ffad985978d48a7bf5d76e30 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 9 Sep 2025 16:27:55 +0300 Subject: [PATCH 115/276] msgconv/from-whatsapp: add action message metadata to disappearing notices --- go.mod | 2 +- go.sum | 4 ++-- pkg/msgconv/wa-misc.go | 12 ++++++++++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index b0f098e..eb8a331 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( golang.org/x/sync v0.16.0 google.golang.org/protobuf v1.36.7 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.25.1-0.20250901150120-f8c3a95de7a1 + maunium.net/go/mautrix v0.25.1-0.20250909132418-41bbe4ace4c7 ) require ( diff --git a/go.sum b/go.sum index 773f393..5bd26b7 100644 --- a/go.sum +++ b/go.sum @@ -101,5 +101,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.1-0.20250901150120-f8c3a95de7a1 h1:6eF+jMxhKxKADdJCHQ9SkmY8+xBpjObRfOOHwHgqrqg= -maunium.net/go/mautrix v0.25.1-0.20250901150120-f8c3a95de7a1/go.mod h1:pDd6Ppg+1PbWrw/rg4ZQQfVYZICRGzH+DcliZ/BODvU= +maunium.net/go/mautrix v0.25.1-0.20250909132418-41bbe4ace4c7 h1:TywnMUPZakwE9oU1jPC8pVZk3q5dP66LkyOQ5Z+e7cI= +maunium.net/go/mautrix v0.25.1-0.20250909132418-41bbe4ace4c7/go.mod h1:pDd6Ppg+1PbWrw/rg4ZQQfVYZICRGzH+DcliZ/BODvU= diff --git a/pkg/msgconv/wa-misc.go b/pkg/msgconv/wa-misc.go index 6120d77..4cc7254 100644 --- a/pkg/msgconv/wa-misc.go +++ b/pkg/msgconv/wa-misc.go @@ -139,8 +139,16 @@ func (mc *MessageConverter) convertEphemeralSettingMessage(ctx context.Context, content.Body += ", but the change was ignored." } return &bridgev2.ConvertedMessagePart{ - Type: event.EventMessage, - Content: content, + Type: event.EventMessage, + Content: content, + Extra: map[string]any{ + "com.beeper.action_message": map[string]any{ + "type": "disappearing_timer", + "timer": disappear.Timer.Milliseconds(), + "timer_type": disappear.Type, + "implicit": false, + }, + }, DontBridge: dontBridge, }, nil } From c6798ebfeae530f787c85711543043f60c661091 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 12 Sep 2025 11:42:23 +0300 Subject: [PATCH 116/276] 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 eb8a331..cca20e6 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( golang.org/x/sync v0.16.0 google.golang.org/protobuf v1.36.7 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.25.1-0.20250909132418-41bbe4ace4c7 + maunium.net/go/mautrix v0.25.1-0.20250911181014-4603a344ce1d ) require ( diff --git a/go.sum b/go.sum index 5bd26b7..b5b0598 100644 --- a/go.sum +++ b/go.sum @@ -101,5 +101,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.1-0.20250909132418-41bbe4ace4c7 h1:TywnMUPZakwE9oU1jPC8pVZk3q5dP66LkyOQ5Z+e7cI= -maunium.net/go/mautrix v0.25.1-0.20250909132418-41bbe4ace4c7/go.mod h1:pDd6Ppg+1PbWrw/rg4ZQQfVYZICRGzH+DcliZ/BODvU= +maunium.net/go/mautrix v0.25.1-0.20250911181014-4603a344ce1d h1:jKAn+2wwFC3aLdgE7IBSxhVjB4JisBOz2oCLzj928+c= +maunium.net/go/mautrix v0.25.1-0.20250911181014-4603a344ce1d/go.mod h1:pDd6Ppg+1PbWrw/rg4ZQQfVYZICRGzH+DcliZ/BODvU= From 05b8c63efd788d470992dadb86d85802172655d8 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 12 Sep 2025 11:53:39 +0300 Subject: [PATCH 117/276] legacyprovision: temporarily restore start chat endpoints --- cmd/mautrix-whatsapp/legacyprovision.go | 112 ++++++++++++++++++++++++ cmd/mautrix-whatsapp/main.go | 7 ++ 2 files changed, 119 insertions(+) create mode 100644 cmd/mautrix-whatsapp/legacyprovision.go diff --git a/cmd/mautrix-whatsapp/legacyprovision.go b/cmd/mautrix-whatsapp/legacyprovision.go new file mode 100644 index 0000000..2a8479d --- /dev/null +++ b/cmd/mautrix-whatsapp/legacyprovision.go @@ -0,0 +1,112 @@ +package main + +import ( + "net/http" + "strings" + + "github.com/rs/zerolog/hlog" + "go.mau.fi/util/exhttp" + "go.mau.fi/whatsmeow/types" + "maunium.net/go/mautrix/bridgev2" + "maunium.net/go/mautrix/bridgev2/matrix" + "maunium.net/go/mautrix/id" + + "go.mau.fi/mautrix-whatsapp/pkg/connector" + "go.mau.fi/mautrix-whatsapp/pkg/waid" +) + +type OtherUserInfo struct { + MXID id.UserID `json:"mxid"` + JID types.JID `json:"jid"` + Name string `json:"displayname"` + Avatar id.ContentURIString `json:"avatar_url"` +} + +type PortalInfo struct { + RoomID id.RoomID `json:"room_id"` + OtherUser *OtherUserInfo `json:"other_user,omitempty"` + GroupInfo *types.GroupInfo `json:"group_info,omitempty"` + JustCreated bool `json:"just_created"` +} + +type Error struct { + Success bool `json:"success"` + Error string `json:"error"` + ErrCode string `json:"errcode"` +} + +func legacyProvContacts(w http.ResponseWriter, r *http.Request) { + userLogin := m.Matrix.Provisioning.GetLoginForRequest(w, r) + if userLogin == nil { + return + } + if contacts, err := userLogin.Client.(*connector.WhatsAppClient).Device.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", + ErrCode: "failed to get contacts", + }) + } else { + augmentedContacts := map[types.JID]any{} + for jid, contact := range contacts { + var avatarURL id.ContentURIString + if puppet, _ := m.Bridge.GetExistingGhostByID(r.Context(), waid.MakeUserID(jid)); puppet != nil { + avatarURL = puppet.AvatarMXC + } + augmentedContacts[jid] = map[string]interface{}{ + "Found": contact.Found, + "FirstName": contact.FirstName, + "FullName": contact.FullName, + "PushName": contact.PushName, + "BusinessName": contact.BusinessName, + "AvatarURL": avatarURL, + } + } + exhttp.WriteJSONResponse(w, http.StatusOK, augmentedContacts) + } +} + +func legacyProvResolveIdentifier(w http.ResponseWriter, r *http.Request) { + number := r.PathValue("number") + userLogin := m.Matrix.Provisioning.GetLoginForRequest(w, r) + if userLogin == nil { + return + } + startChat := strings.Contains(r.URL.Path, "/v1/pm/") + resp, err := userLogin.Client.(*connector.WhatsAppClient).ResolveIdentifier(r.Context(), number, startChat) + if err != nil { + hlog.FromRequest(r).Warn().Err(err).Str("identifier", number).Msg("Failed to resolve identifier") + matrix.RespondWithError(w, err, "Internal error resolving identifier") + return + } + var portal *bridgev2.Portal + if startChat { + portal, err = m.Bridge.GetPortalByKey(r.Context(), resp.Chat.PortalKey) + if err != nil { + hlog.FromRequest(r).Warn().Err(err).Stringer("portal_key", resp.Chat.PortalKey).Msg("Failed to get portal by key") + matrix.RespondWithError(w, err, "Internal error getting portal by key") + return + } + err = portal.CreateMatrixRoom(r.Context(), userLogin, nil) + if err != nil { + hlog.FromRequest(r).Warn().Err(err).Stringer("portal_key", resp.Chat.PortalKey).Msg("Failed to create matrix room for portal") + matrix.RespondWithError(w, err, "Internal error creating matrix room for portal") + return + } + } else { + portal, _ = m.Bridge.GetExistingPortalByKey(r.Context(), resp.Chat.PortalKey) + } + var roomID id.RoomID + if portal != nil { + roomID = portal.MXID + } + exhttp.WriteJSONResponse(w, http.StatusOK, PortalInfo{ + RoomID: roomID, + OtherUser: &OtherUserInfo{ + JID: waid.ParseUserID(resp.UserID), + MXID: resp.Ghost.Intent.GetMXID(), + Name: resp.Ghost.Name, + Avatar: resp.Ghost.AvatarMXC, + }, + }) +} diff --git a/cmd/mautrix-whatsapp/main.go b/cmd/mautrix-whatsapp/main.go index 9dbf088..2564071 100644 --- a/cmd/mautrix-whatsapp/main.go +++ b/cmd/mautrix-whatsapp/main.go @@ -23,6 +23,13 @@ var m = mxmain.BridgeMain{ } func main() { + m.PostStart = func() { + if m.Matrix.Provisioning != nil { + 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.InitVersion(Tag, Commit, BuildTime) m.Run() } From 5d352f464abf69721d484d98c03ca134a2929ab9 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 12 Sep 2025 15:15:48 +0300 Subject: [PATCH 118/276] config: add support for redacted phone in displayname template --- go.mod | 7 +++++-- go.sum | 20 ++++++++++++++++---- pkg/connector/commands.go | 2 +- pkg/connector/config.go | 13 +++++++++---- pkg/connector/example-config.yaml | 11 ++++++----- 5 files changed, 37 insertions(+), 16 deletions(-) diff --git a/go.mod b/go.mod index cca20e6..8958b2c 100644 --- a/go.mod +++ b/go.mod @@ -7,9 +7,9 @@ toolchain go1.25.0 require ( github.com/lib/pq v1.10.9 github.com/rs/zerolog v1.34.0 - go.mau.fi/util v0.9.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-20250829123043-72d2ed58e998 + go.mau.fi/whatsmeow v0.0.0-20250912121005-de128e174913 golang.org/x/image v0.30.0 golang.org/x/net v0.43.0 golang.org/x/sync v0.16.0 @@ -20,8 +20,10 @@ require ( require ( filippo.io/edwards25519 v1.1.0 // indirect + github.com/beeper/argo-go v1.1.2 // indirect github.com/coder/websocket v1.8.13 // indirect 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 @@ -36,6 +38,7 @@ require ( github.com/tidwall/match v1.1.1 // 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 github.com/yuin/goldmark v1.7.13 // indirect go.mau.fi/libsignal v0.2.0 // indirect go.mau.fi/zeroconfig v0.2.0 // indirect diff --git a/go.sum b/go.sum index b5b0598..3c81a2b 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,12 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= 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= +github.com/agnivade/levenshtein v1.2.1/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= +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.13 h1:f3QZdXy7uGVz+4uCJy2nTZyM0yTBj8yANEHhqlXZ9FE= github.com/coder/websocket v1.8.13/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= @@ -9,6 +15,8 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV 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.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -47,6 +55,8 @@ 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/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= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= @@ -61,16 +71,18 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= 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= 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.0 h1:ya3s3pX+Y8R2fgp0DbE7a0o3FwncoelDX5iyaeVE8ls= -go.mau.fi/util v0.9.0/go.mod h1:pdL3lg2aaeeHIreGXNnPwhJPXkXdc3ZxsI6le8hOWEA= +go.mau.fi/util v0.9.1-0.20250912114103-419604f95907 h1:VBApThdwZPIaNKt2sdfTk2Pal2Qqcaokjpq9e/LkVmg= +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-20250829123043-72d2ed58e998 h1:d6Utj53kD7W3O1actrw40Od7xC2/UUr/LHwbIkamE78= -go.mau.fi/whatsmeow v0.0.0-20250829123043-72d2ed58e998/go.mod h1:xD0DR3s4T6PDd3BzgQG05AzLWxdKCmnvdCP3UuQvn9w= +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/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= diff --git a/pkg/connector/commands.go b/pkg/connector/commands.go index 8868741..ec1b999 100644 --- a/pkg/connector/commands.go +++ b/pkg/connector/commands.go @@ -114,7 +114,7 @@ func fnSync(ce *commands.Event) { }) ce.React("✅") case "groups": - groups, err := wa.Client.GetJoinedGroups() + groups, err := wa.Client.GetJoinedGroups(ce.Ctx) if err != nil { ce.Reply("Failed to get joined groups: %v", err) return diff --git a/pkg/connector/config.go b/pkg/connector/config.go index fd6ac0b..aedb8f9 100644 --- a/pkg/connector/config.go +++ b/pkg/connector/config.go @@ -148,11 +148,11 @@ type DisplaynameParams struct { func (c *Config) FormatDisplayname(jid types.JID, phone string, contact types.ContactInfo) string { var nameBuf strings.Builder - if phone == "" { + if phone == "" && jid.Server == types.DefaultUserServer { phone = "+" + jid.User - if jid.Server != types.DefaultUserServer { - phone = jid.User - } + } + if contact.RedactedPhone == "" && phone != "" { + contact.RedactedPhone = redactPhone(phone) } err := c.displaynameTemplate.Execute(&nameBuf, &DisplaynameParams{ ContactInfo: contact, @@ -171,6 +171,11 @@ func (c *Config) FormatDisplayname(jid types.JID, phone string, contact types.Co return nameBuf.String() } +func redactPhone(phone string) string { + // This doesn't keep 2+ digit country codes properly, but whatever + return phone[:2] + strings.Repeat("∙", len(phone)-4) + phone[len(phone)-2:] +} + func (wa *WhatsAppConnector) GetConfig() (string, any, up.Upgrader) { return ExampleConfig, &wa.Config, &up.StructUpgrader{ SimpleUpgrader: up.SimpleUpgrader(upgradeConfig), diff --git a/pkg/connector/example-config.yaml b/pkg/connector/example-config.yaml index 6b9e2e4..99162b3 100644 --- a/pkg/connector/example-config.yaml +++ b/pkg/connector/example-config.yaml @@ -13,11 +13,12 @@ get_proxy_url: null proxy_only_login: false # Displayname template for WhatsApp users. -# {{.PushName}} - nickname set by the WhatsApp user -# {{.BusinessName}} - validated WhatsApp business name -# {{.Phone}} - phone number (international format) -# {{.FullName}} - Name you set in the contacts list -displayname_template: "{{or .BusinessName .PushName .Phone}} (WA)" +# {{.PushName}} - nickname set by the WhatsApp user +# {{.BusinessName}} - validated WhatsApp business name +# {{.Phone}} - phone number (international format) +# {{.RedactedPhone}} - phone number with middle digits replaced by "∙" +# {{.FullName}} - Name you set in the contacts list +displayname_template: '{{or .BusinessName .PushName .Phone .RedactedPhone "Unknown user"}} (WA)' # Should incoming calls send a message to the Matrix room? call_start_notices: true From 2b0c291c49a21e8ae7345ae32a4d9a72c90cf758 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 16 Sep 2025 15:05:03 +0300 Subject: [PATCH 119/276] Bump version to v0.12.5 --- CHANGELOG.md | 13 +++++++-- cmd/mautrix-whatsapp/main.go | 2 +- go.mod | 28 +++++++++--------- go.sum | 56 ++++++++++++++++++------------------ 4 files changed, 54 insertions(+), 45 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef09e27..d32234d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,17 +1,26 @@ -# v0.12.5 (unreleased) +# v0.12.5 (2025-09-16) * Removed legacy provisioning API and database legacy migration. Upgrading directly from versions prior to v0.11.0 is not supported. * If you've been using the bridge since before v0.11.0 and have prevented the bridge from writing to the config, you must either update the config manually or allow the bridge to update it for you **before** upgrading to - this release. + this release (i.e. run v0.12.4 once with config writing allowed). +* Added support for changing group name/topic/avatar from Matrix + (thanks to [@Petersmit27] in [#834]). +* Added `RedactedPhone` placeholder for displayname templates. This allows + community announcement groups (where you can't see participants phone numbers) + to have better names than random numbers. * Added support for `com.beeper.disappearing_timer` state event, which stores the disappearing setting of chats and allows changing the setting from Matrix. * Added lottieconverter to Docker images to enable converting animated stickers from WhatsApp. +* Added support for creating WhatsApp groups. * Fixed sent PNGs not being rendered on WhatsApp iOS. +[@Petersmit27]: https://github.com/Petersmit27 +[#834]: https://github.com/mautrix/whatsapp/pull/834 + # v0.12.4 (2025-08-16) * Deprecated legacy provisioning API. The `/_matrix/provision/v1` endpoints will diff --git a/cmd/mautrix-whatsapp/main.go b/cmd/mautrix-whatsapp/main.go index 2564071..068e67d 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: "0.12.4", + Version: "0.12.5", Connector: &connector.WhatsAppConnector{}, } diff --git a/go.mod b/go.mod index 8958b2c..b78e9f6 100644 --- a/go.mod +++ b/go.mod @@ -2,26 +2,26 @@ module go.mau.fi/mautrix-whatsapp go 1.24.0 -toolchain go1.25.0 +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-0.20250912114103-419604f95907 + go.mau.fi/util v0.9.1 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20250912121005-de128e174913 - golang.org/x/image v0.30.0 - golang.org/x/net v0.43.0 - golang.org/x/sync v0.16.0 - google.golang.org/protobuf v1.36.7 + go.mau.fi/whatsmeow v0.0.0-20250916115455-914d640cc83c + 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 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.25.1-0.20250911181014-4603a344ce1d + maunium.net/go/mautrix v0.25.1 ) require ( filippo.io/edwards25519 v1.1.0 // indirect github.com/beeper/argo-go v1.1.2 // indirect - github.com/coder/websocket v1.8.13 // indirect + github.com/coder/websocket v1.8.14 // indirect 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 @@ -30,7 +30,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-20250813065127-a731cc31b4fe // indirect + github.com/petermattis/goid v0.0.0-20250904145737-900bdf8bb490 // 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 @@ -42,10 +42,10 @@ require ( github.com/yuin/goldmark v1.7.13 // indirect go.mau.fi/libsignal v0.2.0 // indirect go.mau.fi/zeroconfig v0.2.0 // indirect - golang.org/x/crypto v0.41.0 // indirect - golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b // indirect - golang.org/x/sys v0.35.0 // indirect - golang.org/x/text v0.28.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 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 3c81a2b..eab6786 100644 --- a/go.sum +++ b/go.sum @@ -8,8 +8,8 @@ github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNg github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= 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.13 h1:f3QZdXy7uGVz+4uCJy2nTZyM0yTBj8yANEHhqlXZ9FE= -github.com/coder/websocket v1.8.13/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= +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/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -42,8 +42,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-20250813065127-a731cc31b4fe h1:vHpqOnPlnkba8iSxU4j/CvDSS9J4+F4473esQsYLGoE= -github.com/petermattis/goid v0.0.0-20250813065127-a731cc31b4fe/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +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/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= @@ -59,8 +59,8 @@ 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= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= 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= @@ -77,33 +77,33 @@ 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-0.20250912114103-419604f95907 h1:VBApThdwZPIaNKt2sdfTk2Pal2Qqcaokjpq9e/LkVmg= -go.mau.fi/util v0.9.1-0.20250912114103-419604f95907/go.mod h1:pdL3lg2aaeeHIreGXNnPwhJPXkXdc3ZxsI6le8hOWEA= +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-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-20250916115455-914d640cc83c h1:G8mT+1CY76BM49kCnw2OQmE2PzNUWjotIv2DxqcykCc= +go.mau.fi/whatsmeow v0.0.0-20250916115455-914d640cc83c/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.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= -golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= -golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b h1:DXr+pvt3nC887026GRP39Ej11UATqWDmWuS99x26cD0= -golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4= -golang.org/x/image v0.30.0 h1:jD5RhkmVAnjqaCUXfbGBrn3lpxbknfN9w2UhHHU+5B4= -golang.org/x/image v0.30.0/go.mod h1:SAEUTxCCMWSrJcCy/4HwavEsfZZJlYxeHLc6tTiAe/c= -golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= -golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +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/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.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= -golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= -google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A= -google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +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= 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.25.1-0.20250911181014-4603a344ce1d h1:jKAn+2wwFC3aLdgE7IBSxhVjB4JisBOz2oCLzj928+c= -maunium.net/go/mautrix v0.25.1-0.20250911181014-4603a344ce1d/go.mod h1:pDd6Ppg+1PbWrw/rg4ZQQfVYZICRGzH+DcliZ/BODvU= +maunium.net/go/mautrix v0.25.1 h1:+xe3eXtQNcDPU/HoWzvSOA5YX57iqlYI1TXf/fM0KWs= +maunium.net/go/mautrix v0.25.1/go.mod h1:iSueLJ/2fBaNrsTObGqi1j0cl/loxrtAjmjay1scYD8= From 8692347211f874af0aeb0a94138b4ca7eca9b909 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 17 Sep 2025 15:04:12 +0300 Subject: [PATCH 120/276] msgconv/from-whatsapp: ignore disappearing timers in backfill --- pkg/connector/backfill.go | 2 +- pkg/connector/events.go | 4 ++-- pkg/msgconv/from-whatsapp.go | 3 ++- pkg/msgconv/wa-misc.go | 27 +++++++++++++++------------ 4 files changed, 20 insertions(+), 16 deletions(-) diff --git a/pkg/connector/backfill.go b/pkg/connector/backfill.go index dce0038..2c3a14e 100644 --- a/pkg/connector/backfill.go +++ b/pkg/connector/backfill.go @@ -528,7 +528,7 @@ func (wa *WhatsAppClient) convertHistorySyncMessage( // TODO use proper intent intent := wa.Main.Bridge.Bot wrapped := &bridgev2.BackfillMessage{ - ConvertedMessage: wa.Main.MsgConv.ToMatrix(ctx, portal, wa.Client, intent, msg, rawMsg, info, isViewOnce, nil), + ConvertedMessage: wa.Main.MsgConv.ToMatrix(ctx, portal, wa.Client, intent, msg, rawMsg, info, isViewOnce, true, nil), Sender: wa.makeEventSender(ctx, info.Sender), ID: waid.MakeMessageID(info.Chat, info.Sender, info.ID), TxnID: networkid.TransactionID(waid.MakeMessageID(info.Chat, info.Sender, info.ID)), diff --git a/pkg/connector/events.go b/pkg/connector/events.go index 2917f91..3a30d36 100644 --- a/pkg/connector/events.go +++ b/pkg/connector/events.go @@ -188,7 +188,7 @@ func (evt *WAMessageEvent) ConvertEdit(ctx context.Context, portal *bridgev2.Por ctx = context.WithValue(ctx, msgconv.ContextKeyEditTargetID, evt.Message.GetProtocolMessage().GetKey().GetID()) cm := evt.wa.Main.MsgConv.ToMatrix( - ctx, portal, evt.wa.Client, intent, editedMsg, evt.MsgEvent.RawMessage, &evt.Info, evt.isViewOnce(), previouslyConvertedPart, + ctx, portal, evt.wa.Client, intent, editedMsg, evt.MsgEvent.RawMessage, &evt.Info, evt.isViewOnce(), false, previouslyConvertedPart, ) if evt.isUndecryptableUpsertSubEvent && isFailedMedia(cm) { evt.postHandle = func() { @@ -265,7 +265,7 @@ func (evt *WAMessageEvent) HandleExisting(ctx context.Context, portal *bridgev2. func (evt *WAMessageEvent) ConvertMessage(ctx context.Context, portal *bridgev2.Portal, intent bridgev2.MatrixAPI) (*bridgev2.ConvertedMessage, error) { evt.wa.EnqueuePortalResync(portal) converted := evt.wa.Main.MsgConv.ToMatrix( - ctx, portal, evt.wa.Client, intent, evt.Message, evt.MsgEvent.RawMessage, &evt.Info, evt.isViewOnce(), nil, + ctx, portal, evt.wa.Client, intent, evt.Message, evt.MsgEvent.RawMessage, &evt.Info, evt.isViewOnce(), false, nil, ) if isFailedMedia(converted) { evt.postHandle = func() { diff --git a/pkg/msgconv/from-whatsapp.go b/pkg/msgconv/from-whatsapp.go index 8e05384..c4be4a4 100644 --- a/pkg/msgconv/from-whatsapp.go +++ b/pkg/msgconv/from-whatsapp.go @@ -137,6 +137,7 @@ func (mc *MessageConverter) ToMatrix( rawWaMsg *waE2E.Message, info *types.MessageInfo, isViewOnce bool, + isBackfill bool, previouslyConvertedPart *bridgev2.ConvertedMessagePart, ) *bridgev2.ConvertedMessage { ctx = context.WithValue(ctx, contextKeyClient, client) @@ -212,7 +213,7 @@ func (mc *MessageConverter) ToMatrix( case waMsg.GroupInviteMessage != nil: 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) + part, contextInfo = mc.convertEphemeralSettingMessage(ctx, waMsg.ProtocolMessage, info.Timestamp, isBackfill) case waMsg.EncCommentMessage != nil: part = failedCommentPart default: diff --git a/pkg/msgconv/wa-misc.go b/pkg/msgconv/wa-misc.go index 4cc7254..f610715 100644 --- a/pkg/msgconv/wa-misc.go +++ b/pkg/msgconv/wa-misc.go @@ -114,7 +114,7 @@ func (mc *MessageConverter) convertGroupInviteMessage(ctx context.Context, info }, msg.GetContextInfo() } -func (mc *MessageConverter) convertEphemeralSettingMessage(ctx context.Context, msg *waE2E.ProtocolMessage, ts time.Time) (*bridgev2.ConvertedMessagePart, *waE2E.ContextInfo) { +func (mc *MessageConverter) convertEphemeralSettingMessage(ctx context.Context, msg *waE2E.ProtocolMessage, ts time.Time, isBackfill bool) (*bridgev2.ConvertedMessagePart, *waE2E.ContextInfo) { portal := getPortal(ctx) portalMeta := portal.Metadata.(*waid.PortalMetadata) disappear := database.DisappearingSetting{ @@ -126,17 +126,19 @@ func (mc *MessageConverter) convertEphemeralSettingMessage(ctx context.Context, } dontBridge := portal.Disappear == disappear content := bridgev2.DisappearingMessageNotice(disappear.Timer, false) - if msg.EphemeralSettingTimestamp == nil || portalMeta.DisappearingTimerSetAt < msg.GetEphemeralSettingTimestamp() { - portalMeta.DisappearingTimerSetAt = msg.GetEphemeralSettingTimestamp() - portal.UpdateDisappearingSetting(ctx, disappear, bridgev2.UpdateDisappearingSettingOpts{ - Sender: getIntent(ctx), - Timestamp: ts, - Implicit: false, - Save: true, - SendNotice: false, - }) - } else { - content.Body += ", but the change was ignored." + if !isBackfill { + if msg.EphemeralSettingTimestamp == nil || portalMeta.DisappearingTimerSetAt < msg.GetEphemeralSettingTimestamp() { + portalMeta.DisappearingTimerSetAt = msg.GetEphemeralSettingTimestamp() + portal.UpdateDisappearingSetting(ctx, disappear, bridgev2.UpdateDisappearingSettingOpts{ + Sender: getIntent(ctx), + Timestamp: ts, + Implicit: false, + Save: true, + SendNotice: false, + }) + } else { + content.Body += ", but the change was ignored." + } } return &bridgev2.ConvertedMessagePart{ Type: event.EventMessage, @@ -147,6 +149,7 @@ func (mc *MessageConverter) convertEphemeralSettingMessage(ctx context.Context, "timer": disappear.Timer.Milliseconds(), "timer_type": disappear.Type, "implicit": false, + "backfill": isBackfill, }, }, DontBridge: dontBridge, From a5181b03ff65b0dadc8ac5c81edeb1d44e074e3e Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 19 Sep 2025 14:32:31 +0300 Subject: [PATCH 121/276] capabilities: enable implicit read receipts --- go.mod | 2 +- go.sum | 4 ++-- pkg/connector/capabilities.go | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index b78e9f6..a83728a 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.9 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.25.1 + maunium.net/go/mautrix v0.25.2-0.20250919113047-b760023dcaa3 ) require ( diff --git a/go.sum b/go.sum index eab6786..d23f26e 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.1 h1:+xe3eXtQNcDPU/HoWzvSOA5YX57iqlYI1TXf/fM0KWs= -maunium.net/go/mautrix v0.25.1/go.mod h1:iSueLJ/2fBaNrsTObGqi1j0cl/loxrtAjmjay1scYD8= +maunium.net/go/mautrix v0.25.2-0.20250919113047-b760023dcaa3 h1:MU7lyDtxVAsd1Mh+WGJOFD5PsGLRRl3Xv67zizxLkgc= +maunium.net/go/mautrix v0.25.2-0.20250919113047-b760023dcaa3/go.mod h1:iSueLJ/2fBaNrsTObGqi1j0cl/loxrtAjmjay1scYD8= diff --git a/pkg/connector/capabilities.go b/pkg/connector/capabilities.go index f9b1925..095459f 100644 --- a/pkg/connector/capabilities.go +++ b/pkg/connector/capabilities.go @@ -16,6 +16,7 @@ import ( var WhatsAppGeneralCaps = &bridgev2.NetworkGeneralCapabilities{ DisappearingMessages: true, AggressiveUpdateInfo: true, + ImplicitReadReceipts: true, Provisioning: bridgev2.ProvisioningCapabilities{ ResolveIdentifier: bridgev2.ResolveIdentifierCapabilities{ CreateDM: true, From f63240298944d47c1d523e85dfc81a7d7825f470 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 19 Sep 2025 15:48:58 +0300 Subject: [PATCH 122/276] handlewhatsapp: reroute LID calls to phone number DM portal --- go.mod | 2 +- go.sum | 4 ++-- pkg/connector/handlewhatsapp.go | 14 +++++++++++--- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index a83728a..06ea745 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-20250916115455-914d640cc83c + go.mau.fi/whatsmeow v0.0.0-20250919124702-c8bdfd36d05e 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 d23f26e..dea6360 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-20250916115455-914d640cc83c h1:G8mT+1CY76BM49kCnw2OQmE2PzNUWjotIv2DxqcykCc= -go.mau.fi/whatsmeow v0.0.0-20250916115455-914d640cc83c/go.mod h1:dvltpCF0rOHbbur25DHbQ3Ovi747z2Pm11S2M7p1T74= +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/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/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index e5e8029..f7c2348 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -88,9 +88,9 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) (success bool) { success = wa.handleWAUndecryptableMessage(ctx, evt) case *events.CallOffer: - success = wa.handleWACallStart(ctx, evt.GroupJID, evt.CallCreator, evt.CallID, "", evt.Timestamp) + success = wa.handleWACallStart(ctx, evt.GroupJID, evt.CallCreator, evt.CallCreatorAlt, evt.CallID, "", evt.Timestamp) case *events.CallOfferNotice: - success = wa.handleWACallStart(ctx, evt.GroupJID, evt.CallCreator, evt.CallID, evt.Type, evt.Timestamp) + success = wa.handleWACallStart(ctx, evt.GroupJID, evt.CallCreator, evt.CallCreatorAlt, evt.CallID, evt.Type, evt.Timestamp) case *events.CallTerminate, *events.CallRelayLatency, *events.CallAccept, *events.UnknownCallEvent: // ignore case *events.IdentityChange: @@ -475,10 +475,18 @@ func (wa *WhatsAppClient) handleWALogout(reason events.ConnectFailureReason, onC const callEventMaxAge = 15 * time.Minute -func (wa *WhatsAppClient) handleWACallStart(ctx context.Context, group, sender types.JID, id, callType string, ts time.Time) bool { +func (wa *WhatsAppClient) handleWACallStart(ctx context.Context, group, sender, senderAlt types.JID, id, callType string, ts time.Time) bool { if !wa.Main.Config.CallStartNotices || time.Since(ts) > callEventMaxAge { return true } + if sender.Server == types.HiddenUserServer && senderAlt.Server == types.DefaultUserServer { + wa.UserLogin.Log.Debug(). + Stringer("lid", sender). + Stringer("pn", senderAlt). + Str("call_id", id). + Msg("Forced LID caller to phone number in incoming call") + sender, senderAlt = senderAlt, sender + } chat := group if chat.IsEmpty() { chat = sender From c2c98f273679f96bd3d083d852dde253ae5c1641 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 24 Sep 2025 20:30:20 +0300 Subject: [PATCH 123/276] 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 06ea745..624d7ac 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.9 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.25.2-0.20250919113047-b760023dcaa3 + maunium.net/go/mautrix v0.25.2-0.20250924172949-cf29b07f32ce ) require ( diff --git a/go.sum b/go.sum index dea6360..06e3688 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-0.20250919113047-b760023dcaa3 h1:MU7lyDtxVAsd1Mh+WGJOFD5PsGLRRl3Xv67zizxLkgc= -maunium.net/go/mautrix v0.25.2-0.20250919113047-b760023dcaa3/go.mod h1:iSueLJ/2fBaNrsTObGqi1j0cl/loxrtAjmjay1scYD8= +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= From a39d89c9d848b3f0862dab1f68517f405c070c1b Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 25 Sep 2025 20:24:15 +0300 Subject: [PATCH 124/276] handlematrix: allow resyncing DM portals on viewing chat event --- pkg/connector/client.go | 2 +- pkg/connector/events.go | 2 +- pkg/connector/userinfo.go | 6 ++++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/pkg/connector/client.go b/pkg/connector/client.go index aefa5c6..dc160ba 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -423,7 +423,7 @@ func (wa *WhatsAppClient) HandleMatrixViewingChat(ctx context.Context, msg *brid // Reset, but don't save, portal last sync time for immediate sync now msg.Portal.Metadata.(*waid.PortalMetadata).LastSync.Time = time.Time{} // Enqueue for the sync, don't block on it completing - wa.EnqueuePortalResync(msg.Portal) + wa.EnqueuePortalResync(msg.Portal, true) if msg.Portal.OtherUserID != "" { // If this is a DM, also sync the ghost of the other user immediately diff --git a/pkg/connector/events.go b/pkg/connector/events.go index 3a30d36..46545fd 100644 --- a/pkg/connector/events.go +++ b/pkg/connector/events.go @@ -263,7 +263,7 @@ func (evt *WAMessageEvent) HandleExisting(ctx context.Context, portal *bridgev2. } func (evt *WAMessageEvent) ConvertMessage(ctx context.Context, portal *bridgev2.Portal, intent bridgev2.MatrixAPI) (*bridgev2.ConvertedMessage, error) { - evt.wa.EnqueuePortalResync(portal) + evt.wa.EnqueuePortalResync(portal, false) converted := evt.wa.Main.MsgConv.ToMatrix( ctx, portal, evt.wa.Client, intent, evt.Message, evt.MsgEvent.RawMessage, &evt.Info, evt.isViewOnce(), false, nil, ) diff --git a/pkg/connector/userinfo.go b/pkg/connector/userinfo.go index bfa9865..74ffc27 100644 --- a/pkg/connector/userinfo.go +++ b/pkg/connector/userinfo.go @@ -48,9 +48,11 @@ func (wa *WhatsAppClient) EnqueueGhostResync(ghost *bridgev2.Ghost) { wa.resyncQueueLock.Unlock() } -func (wa *WhatsAppClient) EnqueuePortalResync(portal *bridgev2.Portal) { +func (wa *WhatsAppClient) EnqueuePortalResync(portal *bridgev2.Portal, allowDM bool) { jid, _ := waid.ParsePortalID(portal.ID) - if jid.Server != types.GroupServer || portal.Metadata.(*waid.PortalMetadata).LastSync.Add(ResyncMinInterval).After(time.Now()) { + if portal.Metadata.(*waid.PortalMetadata).LastSync.Add(ResyncMinInterval).After(time.Now()) { + return + } else if !allowDM && jid.Server != types.GroupServer { return } wa.resyncQueueLock.Lock() From 9482665e9503a9df24c9106adbfd8449c15d8cf2 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 25 Sep 2025 20:27:28 +0300 Subject: [PATCH 125/276] {chat,user}info: always save last sync ts if min interval is less than a day --- pkg/connector/chatinfo.go | 2 +- pkg/connector/userinfo.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/connector/chatinfo.go b/pkg/connector/chatinfo.go index 58855b5..5a8bfcf 100644 --- a/pkg/connector/chatinfo.go +++ b/pkg/connector/chatinfo.go @@ -75,7 +75,7 @@ func (wa *WhatsAppClient) addExtrasToWrapped(ctx context.Context, portalJID type func updatePortalLastSyncAt(_ context.Context, portal *bridgev2.Portal) bool { meta := portal.Metadata.(*waid.PortalMetadata) - forceSave := time.Since(meta.LastSync.Time) > 24*time.Hour + forceSave := ResyncMinInterval < 24*time.Hour || time.Since(meta.LastSync.Time) > 24*time.Hour meta.LastSync = jsontime.UnixNow() return forceSave } diff --git a/pkg/connector/userinfo.go b/pkg/connector/userinfo.go index 74ffc27..c9e2e9f 100644 --- a/pkg/connector/userinfo.go +++ b/pkg/connector/userinfo.go @@ -245,7 +245,7 @@ func (wa *WhatsAppClient) contactToUserInfo(ctx context.Context, jid types.JID, func updateGhostLastSyncAt(_ context.Context, ghost *bridgev2.Ghost) bool { meta := ghost.Metadata.(*waid.GhostMetadata) - forceSave := time.Since(meta.LastSync.Time) > 24*time.Hour + forceSave := ResyncMinInterval < 24*time.Hour || time.Since(meta.LastSync.Time) > 24*time.Hour meta.LastSync = jsontime.UnixNow() return forceSave } From 462bce6f3761d6db2b8edc48965f05ae71782bc6 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 29 Sep 2025 19:23:13 +0300 Subject: [PATCH 126/276] 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 127/276] 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 128/276] 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 129/276] 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 130/276] 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 131/276] 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 132/276] 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 133/276] 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 134/276] 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 135/276] 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 136/276] 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 137/276] 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 138/276] 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 139/276] 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 140/276] 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 141/276] 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 142/276] 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 143/276] 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 144/276] 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 145/276] 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 146/276] 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 147/276] 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 148/276] 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 149/276] 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 150/276] 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 151/276] 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 152/276] 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 153/276] 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 154/276] 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 155/276] 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 156/276] 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 157/276] 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 158/276] 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 159/276] 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 160/276] 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 161/276] 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 162/276] 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 163/276] 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 164/276] 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 165/276] 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 166/276] 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 167/276] 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 168/276] 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 169/276] 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 170/276] 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 171/276] 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 172/276] 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 173/276] 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 174/276] 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 175/276] 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 176/276] 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 177/276] 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 178/276] 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 179/276] 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 180/276] 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 181/276] 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 182/276] 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 183/276] 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 184/276] 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 185/276] 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 186/276] 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 187/276] 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 188/276] 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 189/276] 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 190/276] 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 191/276] 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 192/276] 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 193/276] 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 194/276] 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 195/276] 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 196/276] 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 197/276] 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 198/276] 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 199/276] 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 200/276] 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 201/276] 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 202/276] 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 203/276] 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 204/276] 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 205/276] 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 206/276] 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 207/276] 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 208/276] 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 209/276] 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 210/276] 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 211/276] 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 212/276] 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 213/276] 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 214/276] 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 215/276] 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 216/276] 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 217/276] 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 218/276] 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 219/276] 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 220/276] 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 221/276] 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 222/276] 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 223/276] 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 224/276] 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 225/276] 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 226/276] 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 227/276] 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 228/276] 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 229/276] 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 230/276] 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 231/276] 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 232/276] 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 233/276] 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 234/276] 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 235/276] 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 236/276] 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 237/276] 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 238/276] 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 239/276] 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 240/276] 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 241/276] 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 242/276] 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 243/276] 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 244/276] 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 245/276] 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 246/276] 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 247/276] 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 248/276] 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 249/276] 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 250/276] .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 251/276] 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 252/276] 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 253/276] 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 254/276] 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 255/276] 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 256/276] 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 257/276] 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 258/276] 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 259/276] 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 260/276] 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 261/276] 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 262/276] .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 263/276] 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 264/276] 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 265/276] .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 266/276] 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 267/276] 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 268/276] 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 269/276] 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 270/276] 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 271/276] 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 272/276] 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 273/276] 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 274/276] 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 275/276] 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 276/276] 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 {