From cbe182fcb1082d5f24225ee812b39d9c2c14db70 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 19 May 2026 17:54:05 +0300 Subject: [PATCH 01/29] 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 c52b925..89dc3a7 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.28.0 + maunium.net/go/mautrix v0.28.1-0.20260519145316-34b5f49408a3 ) require ( diff --git a/go.sum b/go.sum index 5fe1561..bfe01a5 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.28.0 h1:vBakLzf8MAdfED3NzAKiMeKQbc3AQ4EAS03NC+TVMXQ= -maunium.net/go/mautrix v0.28.0/go.mod h1:/a9A7LGaqb9B3nho4tLd28n0EPcCdwpm2dxkxkLLgh0= +maunium.net/go/mautrix v0.28.1-0.20260519145316-34b5f49408a3 h1:l86igJY8Te5JEBNPL9NSpZaGv6eHsxtCrAabXv8KPJo= +maunium.net/go/mautrix v0.28.1-0.20260519145316-34b5f49408a3/go.mod h1:/a9A7LGaqb9B3nho4tLd28n0EPcCdwpm2dxkxkLLgh0= From b46b41edf882059131bfcdd4dbecb3ab8ea808d5 Mon Sep 17 00:00:00 2001 From: Kishan Bagaria <1093313+KishanBagaria@users.noreply.github.com> Date: Tue, 19 May 2026 20:51:26 +0530 Subject: [PATCH 02/29] handlewhatsapp,msgconv,directmedia: fix HD media replacements (#920) Co-authored-by: Tulir Asokan --- pkg/connector/directmedia.go | 4 ++++ pkg/connector/events.go | 3 ++- pkg/connector/handlewhatsapp.go | 4 ++++ pkg/msgconv/wa-media.go | 9 ++++++++- pkg/waid/id.go | 7 ++++--- pkg/waid/mediaid.go | 12 ++++++++++-- 6 files changed, 32 insertions(+), 7 deletions(-) diff --git a/pkg/connector/directmedia.go b/pkg/connector/directmedia.go index ac2fbd2..70289cd 100644 --- a/pkg/connector/directmedia.go +++ b/pkg/connector/directmedia.go @@ -17,6 +17,7 @@ package connector import ( + "bytes" "context" "encoding/json" "errors" @@ -171,6 +172,9 @@ func (wa *WhatsAppConnector) downloadMessageDirectMedia(ctx context.Context, par if err != nil { return nil, fmt.Errorf("failed to unmarshal media keys: %w", err) } + if version := parsedID.Message.Version; len(version) > 0 && !bytes.Equal(version, keys.EncSHA256) && !bytes.Equal(version, keys.SHA256) { + return nil, mautrix.MNotFound.WithMessage("Version mismatch, media was likely replaced") + } var ul *bridgev2.UserLogin if parsedID.UserLogin != "" { ul = wa.Bridge.GetCachedUserLoginByID(parsedID.UserLogin) diff --git a/pkg/connector/events.go b/pkg/connector/events.go index de5bdb8..3e809b7 100644 --- a/pkg/connector/events.go +++ b/pkg/connector/events.go @@ -91,6 +91,7 @@ type WAMessageEvent struct { parsedMessageType string isUndecryptableUpsertSubEvent bool + dontRenderEdited bool postHandle func() } @@ -204,7 +205,7 @@ func (evt *WAMessageEvent) ConvertEdit(ctx context.Context, portal *bridgev2.Por } } editPart := cm.Parts[0].ToEditPart(existing[0]) - if evt.isUndecryptableUpsertSubEvent { + if evt.isUndecryptableUpsertSubEvent || evt.dontRenderEdited { if editPart.TopLevelExtra == nil { editPart.TopLevelExtra = make(map[string]any) } diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index 3abcede..79871b3 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -359,6 +359,7 @@ func (wa *WhatsAppClient) handleWAMessage(ctx context.Context, evt *events.Messa return } + dontRenderEdited := false 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() @@ -377,6 +378,8 @@ func (wa *WhatsAppClient) handleWAMessage(ctx context.Context, evt *events.Messa evt.Message = &waE2E.Message{ ProtocolMessage: protocolMsg, } + parsedMessageType = getMessageType(evt.Message) + dontRenderEdited = true } else if assocType == waE2E.MessageAssociation_MOTION_PHOTO { //evt.Message = evt.Message.GetAssociatedChildMessage().GetMessage() wa.UserLogin.Log.Debug(). @@ -395,6 +398,7 @@ func (wa *WhatsAppClient) handleWAMessage(ctx context.Context, evt *events.Messa MsgEvent: evt, parsedMessageType: parsedMessageType, + dontRenderEdited: dontRenderEdited, }) return res.Success } diff --git a/pkg/msgconv/wa-media.go b/pkg/msgconv/wa-media.go index c2a8624..38fe9e2 100644 --- a/pkg/msgconv/wa-media.go +++ b/pkg/msgconv/wa-media.go @@ -90,7 +90,7 @@ func (mc *MessageConverter) convertMediaMessage( var err error portal := getPortal(ctx) idOverride := getEditTargetID(ctx) - preparedMedia.URL, err = portal.Bridge.Matrix.GenerateContentURI(ctx, waid.MakeMediaID(messageInfo, idOverride, portal.Receiver)) + preparedMedia.URL, err = portal.Bridge.Matrix.GenerateContentURI(ctx, waid.MakeMediaID(messageInfo, idOverride, portal.Receiver, getMediaIDVersion(msg))) if err != nil { panic(fmt.Errorf("failed to generate content URI: %w", err)) } @@ -119,6 +119,13 @@ func (mc *MessageConverter) convertMediaMessage( return } +func getMediaIDVersion(msg MediaMessage) []byte { + if encSHA256 := msg.GetFileEncSHA256(); len(encSHA256) > 0 { + return encSHA256 + } + return msg.GetFileSHA256() +} + func (mc *MessageConverter) convertAlbumMessage(ctx context.Context, msg *waE2E.AlbumMessage) (*bridgev2.ConvertedMessagePart, *waE2E.ContextInfo) { parts := make([]string, 0, 2) if msg.GetExpectedImageCount() > 0 { diff --git a/pkg/waid/id.go b/pkg/waid/id.go index 840e0f1..e522f5f 100644 --- a/pkg/waid/id.go +++ b/pkg/waid/id.go @@ -88,9 +88,10 @@ func MakeFakeMessageID(chat, sender types.JID, data string) networkid.MessageID } type ParsedMessageID struct { - Chat types.JID - Sender types.JID - ID types.MessageID + Chat types.JID + Sender types.JID + ID types.MessageID + Version []byte } func (pmi *ParsedMessageID) String() networkid.MessageID { diff --git a/pkg/waid/mediaid.go b/pkg/waid/mediaid.go index 093ece1..716e187 100644 --- a/pkg/waid/mediaid.go +++ b/pkg/waid/mediaid.go @@ -36,7 +36,7 @@ const ( mediaIDTypeStickerPackItem = 252 ) -func MakeMediaID(messageInfo *types.MessageInfo, idOverride types.MessageID, receiver networkid.UserLoginID) networkid.MediaID { +func MakeMediaID(messageInfo *types.MessageInfo, idOverride types.MessageID, receiver networkid.UserLoginID, version []byte) networkid.MediaID { compactChat := compactJID(messageInfo.Chat.ToNonAD()) compactSender := compactJID(messageInfo.Sender.ToNonAD()) receiverID := compactJID(ParseUserLoginID(receiver, 0)) @@ -46,7 +46,7 @@ func MakeMediaID(messageInfo *types.MessageInfo, idOverride types.MessageID, rec } else { compactID = compactMsgID(messageInfo.ID) } - mediaID := make([]byte, 0, 5+len(compactChat)+len(compactSender)+len(receiverID)+len(compactID)) + mediaID := make([]byte, 0, 6+len(compactChat)+len(compactSender)+len(receiverID)+len(compactID)+len(version)) mediaID = append(mediaID, mediaIDTypeMessage) mediaID = append(mediaID, byte(len(compactChat))) mediaID = append(mediaID, compactChat...) @@ -56,6 +56,8 @@ func MakeMediaID(messageInfo *types.MessageInfo, idOverride types.MessageID, rec mediaID = append(mediaID, receiverID...) mediaID = append(mediaID, byte(len(compactID))) mediaID = append(mediaID, compactID...) + mediaID = append(mediaID, byte(len(version))) + mediaID = append(mediaID, version...) return mediaID } @@ -138,6 +140,12 @@ func ParseMediaID(mediaID networkid.MediaID) (*ParsedMediaID, error) { Sender: senderJID, ID: id, } + if len(mediaID) > 0 { + parsed.Message.Version, err = readCompact(&mediaID, rawBytes) + if err != nil { + return nil, fmt.Errorf("failed to parse version: %w", err) + } + } parsed.UserLogin = MakeUserLoginID(receiverID) case mediaIDTypeAvatar, mediaIDTypeCommunityAvatar: targetJID, err := readCompact(&mediaID, parseCompactJID) From c74327e8e48dd5f0be3bd36d32909434822d346b Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 21 May 2026 16:49:45 +0300 Subject: [PATCH 03/29] login: use 400 for whatsapp ratelimits --- pkg/connector/login.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/connector/login.go b/pkg/connector/login.go index 88e7b75..b70ab1a 100644 --- a/pkg/connector/login.go +++ b/pkg/connector/login.go @@ -76,8 +76,8 @@ var ( } ErrRateLimitedByWhatsApp = bridgev2.RespError{ ErrCode: "FI.MAU.WHATSAPP.RATE_LIMITED", - Err: "Rate limited by WhatsApp", - StatusCode: http.StatusTooManyRequests, + Err: "Rate limited by WhatsApp. Try again later.", + StatusCode: http.StatusBadRequest, } ) From 9e7dc0bef0066ab536af5382ebac8523190d8b45 Mon Sep 17 00:00:00 2001 From: Kishan Bagaria <1093313+KishanBagaria@users.noreply.github.com> Date: Mon, 25 May 2026 18:10:58 +0530 Subject: [PATCH 04/29] msgconv/from-whatsapp: convert message history notices (#922) Co-authored-by: Tulir Asokan --- pkg/connector/mediarequest.go | 5 +- pkg/connector/wamsgtype.go | 2 + pkg/msgconv/from-whatsapp.go | 2 + pkg/msgconv/wa-misc.go | 87 +++++++++++++++++++++++++++++++++++ pkg/waid/dbmeta.go | 7 +++ 5 files changed, 100 insertions(+), 3 deletions(-) diff --git a/pkg/connector/mediarequest.go b/pkg/connector/mediarequest.go index 2c382d1..2686250 100644 --- a/pkg/connector/mediarequest.go +++ b/pkg/connector/mediarequest.go @@ -70,10 +70,9 @@ func (wa *WhatsAppClient) processFailedMedia(ctx context.Context, portalKey netw func (wa *WhatsAppClient) mediaRequestLoop(ctx context.Context) { log := wa.UserLogin.Log.With().Str("loop", "media requests").Logger() ctx = log.WithContext(ctx) - tzName := wa.UserLogin.Metadata.(*waid.UserLoginMetadata).Timezone - userTz, err := time.LoadLocation(tzName) + userTz, err := wa.UserLogin.Metadata.(*waid.UserLoginMetadata).LoadTimezone() var startIn time.Duration - if tzName != "" && err == nil && userTz != nil { + if err == nil && userTz != nil { now := time.Now() startAt := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, userTz) startAt = startAt.Add(time.Duration(wa.Main.Config.HistorySync.MediaRequests.RequestLocalTime) * time.Minute) diff --git a/pkg/connector/wamsgtype.go b/pkg/connector/wamsgtype.go index 92e7f86..a5b65e8 100644 --- a/pkg/connector/wamsgtype.go +++ b/pkg/connector/wamsgtype.go @@ -130,6 +130,8 @@ func getMessageType(waMsg *waE2E.Message) string { return "secret encrypted" case waMsg.PollResultSnapshotMessage != nil: return "poll result snapshot" + case waMsg.MessageHistoryNotice != nil: + return "message history notice" case waMsg.MessageHistoryBundle != nil: return "message history bundle" case waMsg.RequestPhoneNumberMessage != nil: diff --git a/pkg/msgconv/from-whatsapp.go b/pkg/msgconv/from-whatsapp.go index 11569e2..f0e4560 100644 --- a/pkg/msgconv/from-whatsapp.go +++ b/pkg/msgconv/from-whatsapp.go @@ -215,6 +215,8 @@ func (mc *MessageConverter) ToMatrix( part, contextInfo = mc.convertPlaceholderMessage(ctx, waMsg) case waMsg.GroupInviteMessage != nil: part, contextInfo = mc.convertGroupInviteMessage(ctx, info, waMsg.GroupInviteMessage) + case waMsg.MessageHistoryNotice != nil: + part, contextInfo = mc.convertMessageHistoryNotice(ctx, info, waMsg.MessageHistoryNotice) case waMsg.ProtocolMessage != nil && waMsg.ProtocolMessage.GetType() == waE2E.ProtocolMessage_EPHEMERAL_SETTING: part, contextInfo = mc.convertEphemeralSettingMessage(ctx, waMsg.ProtocolMessage, info.Timestamp, isBackfill) case waMsg.EncCommentMessage != nil: diff --git a/pkg/msgconv/wa-misc.go b/pkg/msgconv/wa-misc.go index 7ca4427..71de586 100644 --- a/pkg/msgconv/wa-misc.go +++ b/pkg/msgconv/wa-misc.go @@ -26,6 +26,7 @@ import ( "github.com/rs/zerolog" "go.mau.fi/util/exerrors" + "go.mau.fi/util/exfmt" "go.mau.fi/util/ptr" "go.mau.fi/whatsmeow/proto/waAICommonDeprecated" "go.mau.fi/whatsmeow/proto/waE2E" @@ -117,6 +118,92 @@ func (mc *MessageConverter) convertGroupInviteMessage(ctx context.Context, info }, msg.GetContextInfo() } +func (mc *MessageConverter) formatMessageHistoryNoticeJID(ctx context.Context, jid types.JID) string { + _, displayName, err := mc.getBasicUserInfo(ctx, jid) + if err != nil { + zerolog.Ctx(ctx).Err(err).Stringer("jid", jid).Msg("Failed to get user info for message history notice") + } else if displayName != "" { + return displayName + } + switch jid.Server { + case types.DefaultUserServer: + return "+" + jid.User + default: + return "Unknown user " + jid.String() + } +} + +const maxMessageHistoryNoticeReceivers = 5 + +var others = exfmt.Pluralizable("other") + +func (mc *MessageConverter) formatMessageHistoryNoticeReceivers(ctx context.Context, receivers []string) string { + receiverLimit := min(len(receivers), maxMessageHistoryNoticeReceivers) + receiverNames := make([]string, 0, receiverLimit) + for _, receiver := range receivers[:receiverLimit] { + jid, err := types.ParseJID(receiver) + if err != nil { + zerolog.Ctx(ctx).Err(err).Str("receiver", receiver).Msg("Failed to parse message history receiver JID") + receiverNames = append(receiverNames, receiver) + } else { + receiverNames = append(receiverNames, mc.formatMessageHistoryNoticeJID(ctx, jid)) + } + } + receiverText := strings.Join(receiverNames, ", ") + if len(receivers) > receiverLimit { + receiverText = fmt.Sprintf("%s + %s", receiverText, others(len(receivers)-receiverLimit)) + } + return receiverText +} + +func (mc *MessageConverter) messageHistoryNoticeLocation(ctx context.Context) *time.Location { + portal := getPortal(ctx) + loginID := portal.Receiver + if loginID == "" { + loginID = waid.MakeUserLoginID(getClient(ctx).Store.GetJID().ToNonAD()) + } + if login := mc.Bridge.GetCachedUserLoginByID(loginID); login != nil { + meta, _ := login.Metadata.(*waid.UserLoginMetadata) + loc, err := meta.LoadTimezone() + if err != nil { + zerolog.Ctx(ctx).Err(err).Str("timezone", meta.Timezone).Msg("Failed to load user timezone for message history notice") + } else if loc != nil { + return loc + } + } + return time.Local +} + +func (mc *MessageConverter) convertMessageHistoryNotice(ctx context.Context, info *types.MessageInfo, msg *waE2E.MessageHistoryNotice) (*bridgev2.ConvertedMessagePart, *waE2E.ContextInfo) { + metadata := msg.GetMessageHistoryMetadata() + + sender := mc.formatMessageHistoryNoticeJID(ctx, info.Sender) + body := fmt.Sprintf("%s sent message history", sender) + if receiverText := mc.formatMessageHistoryNoticeReceivers(ctx, metadata.GetHistoryReceivers()); receiverText != "" { + body = fmt.Sprintf("%s to %s", body, receiverText) + } + if count := metadata.GetMessageCount(); count > 0 { + messageWord := "messages" + if count == 1 { + messageWord = "message" + } + body = fmt.Sprintf("%s (%d %s)", body, count, messageWord) + } + if metadata != nil && metadata.OldestMessageTimestampInWindow != nil { + oldestTS := time.Unix(metadata.GetOldestMessageTimestampInWindow(), 0).In(mc.messageHistoryNoticeLocation(ctx)) + body = fmt.Sprintf("%s, starting %s", body, oldestTS.Format("Jan 2, 2006 at 3:04 PM")) + } + body += "." + + return &bridgev2.ConvertedMessagePart{ + Type: event.EventMessage, + Content: &event.MessageEventContent{ + MsgType: event.MsgNotice, + Body: body, + }, + }, msg.GetContextInfo() +} + 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) diff --git a/pkg/waid/dbmeta.go b/pkg/waid/dbmeta.go index 2785105..59e9a9e 100644 --- a/pkg/waid/dbmeta.go +++ b/pkg/waid/dbmeta.go @@ -61,6 +61,13 @@ func (m *UserLoginMetadata) GeneratePushKeys() { } } +func (m *UserLoginMetadata) LoadTimezone() (*time.Location, error) { + if m == nil || m.Timezone == "" { + return nil, nil + } + return time.LoadLocation(m.Timezone) +} + type MessageErrorType string const ( From df86e40e1ef60c11b346b76ae7697e98ca185a5e Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 25 May 2026 15:20:23 +0300 Subject: [PATCH 05/29] config: validate displayname template when loading config --- pkg/connector/config.go | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/pkg/connector/config.go b/pkg/connector/config.go index 2445647..e51c80a 100644 --- a/pkg/connector/config.go +++ b/pkg/connector/config.go @@ -2,6 +2,7 @@ package connector import ( _ "embed" + "fmt" "strings" "text/template" "time" @@ -90,7 +91,15 @@ func (c *Config) UnmarshalYAML(node *yaml.Node) error { func (c *Config) PostProcess() error { var err error c.displaynameTemplate, err = template.New("displayname").Parse(c.DisplaynameTemplate) - return err + if err != nil { + return err + } + // Try to execute template to make sure it's valid + _, err = c.formatDisplayname(types.PSAJID, "", types.ContactInfo{}) + if err != nil { + return fmt.Errorf("failed to execute displayname template: %w", err) + } + return nil } func upgradeConfig(helper up.Helper) { @@ -151,7 +160,7 @@ type DisplaynameParams struct { Short string } -func (c *Config) FormatDisplayname(jid types.JID, phone string, contact types.ContactInfo) string { +func (c *Config) formatDisplayname(jid types.JID, phone string, contact types.ContactInfo) (string, error) { var nameBuf strings.Builder if phone == "" && jid.Server == types.DefaultUserServer { phone = "+" + jid.User @@ -170,13 +179,21 @@ func (c *Config) FormatDisplayname(jid types.JID, phone string, contact types.Co Name: contact.FullName, Short: contact.FirstName, }) + return nameBuf.String(), err +} + +func (c *Config) FormatDisplayname(jid types.JID, phone string, contact types.ContactInfo) string { + name, err := c.formatDisplayname(jid, phone, contact) if err != nil { panic(err) } - return nameBuf.String() + return name } func redactPhone(phone string) string { + if len(phone) <= 4 { + return phone + } // This doesn't keep 2+ digit country codes properly, but whatever return phone[:2] + strings.Repeat("∙", len(phone)-4) + phone[len(phone)-2:] } From 30bbbcaf6f351526c9a43beb4d49ece904bf1e95 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 25 May 2026 15:24:13 +0300 Subject: [PATCH 06/29] msgconv/from-whatsapp: add some html escapes --- pkg/msgconv/wa-location.go | 3 ++- pkg/msgconv/wa-misc.go | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/msgconv/wa-location.go b/pkg/msgconv/wa-location.go index 17efecd..4a1e043 100644 --- a/pkg/msgconv/wa-location.go +++ b/pkg/msgconv/wa-location.go @@ -20,6 +20,7 @@ import ( "bytes" "context" "fmt" + "html" "image" "math" "net/http" @@ -51,7 +52,7 @@ func (mc *MessageConverter) convertLocationMessage(ctx context.Context, msg *waE MsgType: event.MsgLocation, Body: fmt.Sprintf("Location: %s\n%s\n%s", name, msg.GetAddress(), url), Format: event.FormatHTML, - FormattedBody: fmt.Sprintf("Location: %s
%s", url, name, msg.GetAddress()), + FormattedBody: fmt.Sprintf(`Location: %s
%s`, html.EscapeString(url), html.EscapeString(name), html.EscapeString(msg.GetAddress())), GeoURI: fmt.Sprintf("geo:%.5f,%.5f", msg.GetDegreesLatitude(), msg.GetDegreesLongitude()), } diff --git a/pkg/msgconv/wa-misc.go b/pkg/msgconv/wa-misc.go index 71de586..fa495e0 100644 --- a/pkg/msgconv/wa-misc.go +++ b/pkg/msgconv/wa-misc.go @@ -20,6 +20,7 @@ import ( "context" "encoding/base64" "fmt" + "html" "html/template" "strings" "time" @@ -101,7 +102,7 @@ func (mc *MessageConverter) convertGroupInviteMessage(ctx context.Context, info } } - htmlMessage := fmt.Sprintf(template, event.TextToHTML(msg.GetCaption()), msg.GetGroupName(), expiry, mc.Bridge.Config.CommandPrefix) + htmlMessage := fmt.Sprintf(template, event.TextToHTML(msg.GetCaption()), html.EscapeString(msg.GetGroupName()), expiry, mc.Bridge.Config.CommandPrefix) content := &event.MessageEventContent{ MsgType: event.MsgText, Body: format.HTMLToText(htmlMessage), From 5e3172b8168454efc1098071a96550a6bdaa968b Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 25 May 2026 15:33:41 +0300 Subject: [PATCH 07/29] dependencies: update whatsmeow Closes #918 Fixes #901 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 89dc3a7..e38d875 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/tidwall/gjson v1.19.0 go.mau.fi/util v0.9.9 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20260516102357-8d3700152a69 + go.mau.fi/whatsmeow v0.0.0-20260525123251-933deb5f2ee9 golang.org/x/image v0.40.0 golang.org/x/net v0.54.0 golang.org/x/sync v0.20.0 diff --git a/go.sum b/go.sum index bfe01a5..2782985 100644 --- a/go.sum +++ b/go.sum @@ -75,8 +75,8 @@ go.mau.fi/util v0.9.9 h1:ujDeXCo07HBor5oQLyO1tHklupmqVmPgasc53d7q/NE= go.mau.fi/util v0.9.9/go.mod h1:pqt4Vcrt+5gcH/CgrHZg11qSx+b34o6mknGzOEA6waY= 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-20260516102357-8d3700152a69 h1:rRcG2I1LVQ11kH2+KSYxNeFNZXp1oKA5Yxmagsf5bks= -go.mau.fi/whatsmeow v0.0.0-20260516102357-8d3700152a69/go.mod h1:SY+3c678dbtckGZF16M+sfEW8ZxTb9xkaKwhXueF5yE= +go.mau.fi/whatsmeow v0.0.0-20260525123251-933deb5f2ee9 h1:l1E9YRXQhjS7Sm6XbYPq8oiSedSCOB6h3FMqZ8pt+K4= +go.mau.fi/whatsmeow v0.0.0-20260525123251-933deb5f2ee9/go.mod h1:SY+3c678dbtckGZF16M+sfEW8ZxTb9xkaKwhXueF5yE= 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.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI= From a1f7bf9b9318543f1af0aa6dbbee2b8efe5d7d3e Mon Sep 17 00:00:00 2001 From: Kishan Bagaria <1093313+KishanBagaria@users.noreply.github.com> Date: Mon, 25 May 2026 19:02:24 +0530 Subject: [PATCH 08/29] msgconv/from-whatsapp: hide message history bundles (#924) --- pkg/connector/wamsgtype.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pkg/connector/wamsgtype.go b/pkg/connector/wamsgtype.go index a5b65e8..4bcf483 100644 --- a/pkg/connector/wamsgtype.go +++ b/pkg/connector/wamsgtype.go @@ -132,8 +132,6 @@ func getMessageType(waMsg *waE2E.Message) string { return "poll result snapshot" case waMsg.MessageHistoryNotice != nil: return "message history notice" - case waMsg.MessageHistoryBundle != nil: - return "message history bundle" case waMsg.RequestPhoneNumberMessage != nil: return "request phone number" case waMsg.KeepInChatMessage != nil: @@ -154,7 +152,7 @@ func getMessageType(waMsg *waE2E.Message) string { return "chat" case waMsg.PlaceholderMessage != nil: return "placeholder" - case waMsg.SenderKeyDistributionMessage != nil, waMsg.StickerSyncRmrMessage != nil: + case waMsg.SenderKeyDistributionMessage != nil, waMsg.StickerSyncRmrMessage != nil, waMsg.MessageHistoryBundle != nil: return "ignore" default: return "unknown" From 91932df97923c57b074f634f527226a7aadac497 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 27 May 2026 14:00:43 +0300 Subject: [PATCH 09/29] 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 e38d875..0a9d8f8 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/tidwall/gjson v1.19.0 go.mau.fi/util v0.9.9 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20260525123251-933deb5f2ee9 + go.mau.fi/whatsmeow v0.0.0-20260525144132-563bcaa0f632 golang.org/x/image v0.40.0 golang.org/x/net v0.54.0 golang.org/x/sync v0.20.0 diff --git a/go.sum b/go.sum index 2782985..d86e782 100644 --- a/go.sum +++ b/go.sum @@ -75,8 +75,8 @@ go.mau.fi/util v0.9.9 h1:ujDeXCo07HBor5oQLyO1tHklupmqVmPgasc53d7q/NE= go.mau.fi/util v0.9.9/go.mod h1:pqt4Vcrt+5gcH/CgrHZg11qSx+b34o6mknGzOEA6waY= 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-20260525123251-933deb5f2ee9 h1:l1E9YRXQhjS7Sm6XbYPq8oiSedSCOB6h3FMqZ8pt+K4= -go.mau.fi/whatsmeow v0.0.0-20260525123251-933deb5f2ee9/go.mod h1:SY+3c678dbtckGZF16M+sfEW8ZxTb9xkaKwhXueF5yE= +go.mau.fi/whatsmeow v0.0.0-20260525144132-563bcaa0f632 h1:ewBVzZW9EBFoxcZJnR8KCYCNXVHH9leNuF6V+pr8Keg= +go.mau.fi/whatsmeow v0.0.0-20260525144132-563bcaa0f632/go.mod h1:SY+3c678dbtckGZF16M+sfEW8ZxTb9xkaKwhXueF5yE= 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.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI= From 6c6ea098de74d57dff3e45b15dcfa2f049f76e31 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 27 May 2026 19:23:11 +0300 Subject: [PATCH 10/29] 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 0a9d8f8..f38c6d0 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.28.1-0.20260519145316-34b5f49408a3 + maunium.net/go/mautrix v0.28.1-0.20260527103108-d00b9b09fcd5 ) require ( diff --git a/go.sum b/go.sum index d86e782..741ec53 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.28.1-0.20260519145316-34b5f49408a3 h1:l86igJY8Te5JEBNPL9NSpZaGv6eHsxtCrAabXv8KPJo= -maunium.net/go/mautrix v0.28.1-0.20260519145316-34b5f49408a3/go.mod h1:/a9A7LGaqb9B3nho4tLd28n0EPcCdwpm2dxkxkLLgh0= +maunium.net/go/mautrix v0.28.1-0.20260527103108-d00b9b09fcd5 h1:9bx9er6TJxQ2saQw63gwT0HZWg9/0Q30x8V3foKuNBo= +maunium.net/go/mautrix v0.28.1-0.20260527103108-d00b9b09fcd5/go.mod h1:/a9A7LGaqb9B3nho4tLd28n0EPcCdwpm2dxkxkLLgh0= From 7592f9810377ac5052d321d2cceca2a2dee4fe36 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 28 May 2026 17:22:50 +0300 Subject: [PATCH 11/29] chatinfo: add implicit own membership to parent groups --- pkg/connector/chatinfo.go | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/pkg/connector/chatinfo.go b/pkg/connector/chatinfo.go index 5555566..7c7dc8e 100644 --- a/pkg/connector/chatinfo.go +++ b/pkg/connector/chatinfo.go @@ -257,7 +257,7 @@ func (wa *WhatsAppClient) wrapGroupInfo(ctx context.Context, info *types.GroupIn Name: ptr.Ptr(info.Name), Topic: ptr.Ptr(info.Topic), Members: &bridgev2.ChatMemberList{ - IsFull: !info.IsIncognito, + IsFull: !info.IsIncognito && !info.IsParent, TotalMemberCount: len(info.Participants), MemberMap: make(map[networkid.UserID]bridgev2.ChatMember, len(info.Participants)), PowerLevels: &bridgev2.PowerLevelOverrides{ @@ -284,11 +284,15 @@ func (wa *WhatsAppClient) wrapGroupInfo(ctx context.Context, info *types.GroupIn }, ExtraUpdates: extraUpdater, } + var hasSelf bool for _, pcp := range info.Participants { member := bridgev2.ChatMember{ EventSender: wa.makeEventSender(ctx, pcp.JID), Membership: event.MembershipJoin, } + if member.EventSender.IsFromMe { + hasSelf = true + } if pcp.IsSuperAdmin { member.PowerLevel = ptr.Ptr(superAdminPL) } else if pcp.IsAdmin { @@ -299,18 +303,21 @@ func (wa *WhatsAppClient) wrapGroupInfo(ctx context.Context, info *types.GroupIn member.MemberEventExtra = map[string]any{ "com.beeper.exclude_from_timeline": true, } - wrapped.Members.MemberMap[waid.MakeUserID(pcp.JID)] = member + wrapped.Members.MemberMap.Set(member) if pcp.JID.Server == types.HiddenUserServer && !pcp.PhoneNumber.IsEmpty() { - wrapped.Members.MemberMap[waid.MakeUserID(pcp.PhoneNumber)] = bridgev2.ChatMember{ + wrapped.Members.MemberMap.Add(bridgev2.ChatMember{ EventSender: bridgev2.EventSender{Sender: waid.MakeUserID(pcp.PhoneNumber)}, Membership: event.MembershipLeave, PrevMembership: event.MembershipJoin, MemberEventExtra: map[string]any{ "com.beeper.exclude_from_timeline": true, }, - } + }) } } + if info.IsParent && !hasSelf && info.AddressingMode == types.AddressingModeLID { + wrapped.Members.MemberMap.Add(bridgev2.ChatMember{EventSender: wa.makeEventSender(ctx, wa.Device.LID)}) + } if !info.LinkedParentJID.IsEmpty() { wrapped.ParentID = ptr.Ptr(waid.MakePortalID(info.LinkedParentJID)) From eefd547259dd4ec166ab07ca66ec42ad6e131f85 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 29 May 2026 13:24:27 +0300 Subject: [PATCH 12/29] dependencies: update whatsmeow --- go.mod | 10 +++++----- go.sum | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index f38c6d0..1265cfc 100644 --- a/go.mod +++ b/go.mod @@ -12,9 +12,9 @@ require ( github.com/tidwall/gjson v1.19.0 go.mau.fi/util v0.9.9 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20260525144132-563bcaa0f632 + go.mau.fi/whatsmeow v0.0.0-20260529101937-a7ea56383ec4 golang.org/x/image v0.40.0 - golang.org/x/net v0.54.0 + golang.org/x/net v0.55.0 golang.org/x/sync v0.20.0 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 @@ -41,12 +41,12 @@ require ( github.com/tidwall/sjson v1.2.5 // indirect github.com/vektah/gqlparser/v2 v2.5.27 // indirect github.com/yuin/goldmark v1.8.2 // indirect - go.mau.fi/libsignal v0.2.1 // indirect + go.mau.fi/libsignal v0.2.2 // indirect go.mau.fi/zeroconfig v0.2.0 // indirect - golang.org/x/crypto v0.51.0 // indirect + golang.org/x/crypto v0.52.0 // indirect golang.org/x/exp v0.0.0-20260508232706-74f9aab9d74a // indirect golang.org/x/mod v0.36.0 // indirect - golang.org/x/sys v0.44.0 // indirect + golang.org/x/sys v0.45.0 // indirect golang.org/x/text v0.37.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect diff --git a/go.sum b/go.sum index 741ec53..b1c29ff 100644 --- a/go.sum +++ b/go.sum @@ -69,31 +69,31 @@ 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.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/libsignal v0.2.2 h1:QV+XdzQkm3x3aSG7FcqfGSZuFXz83pRZPBFaPygHbOU= +go.mau.fi/libsignal v0.2.2/go.mod h1:CRlIQg2J8uYTfDFvNoO8/KcZjs5cey0vbc6oj/bssY0= go.mau.fi/util v0.9.9 h1:ujDeXCo07HBor5oQLyO1tHklupmqVmPgasc53d7q/NE= go.mau.fi/util v0.9.9/go.mod h1:pqt4Vcrt+5gcH/CgrHZg11qSx+b34o6mknGzOEA6waY= 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-20260525144132-563bcaa0f632 h1:ewBVzZW9EBFoxcZJnR8KCYCNXVHH9leNuF6V+pr8Keg= -go.mau.fi/whatsmeow v0.0.0-20260525144132-563bcaa0f632/go.mod h1:SY+3c678dbtckGZF16M+sfEW8ZxTb9xkaKwhXueF5yE= +go.mau.fi/whatsmeow v0.0.0-20260529101937-a7ea56383ec4 h1:PasY/1lMvmSeSxzXLxqgxf95pjwcnTeQ77t4ERiCbY8= +go.mau.fi/whatsmeow v0.0.0-20260529101937-a7ea56383ec4/go.mod h1:9hto2r5yVE5yyNTRrZErKNSflGBKxIplUVXAD3EJFDE= 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.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI= -golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8= +golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= +golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= golang.org/x/exp v0.0.0-20260508232706-74f9aab9d74a h1:+3jdDGGB8NGb1Zktc737jlt3/A5f6UlwSzmvqUuufxw= golang.org/x/exp v0.0.0-20260508232706-74f9aab9d74a/go.mod h1:d2fgXJLVs4dYDHUk5lwMIfzRzSrWCfGZb0ZqeLa/Vcw= golang.org/x/image v0.40.0 h1:Tw4GyDXMo+daZN1znreBRC3VayR1aLFUyUEOLUdW1a8= golang.org/x/image v0.40.0/go.mod h1:uIc348UZMSvS5Z65CVZ7iDPaNobNFEPeJ4kbqTOszmA= golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4= golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ= -golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w= -golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ= +golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= +golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= 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.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ= -golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= From c1ae62ccaed21b5e45013e03be35063b4e4e48c3 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 1 Jun 2026 01:43:49 +0300 Subject: [PATCH 13/29] 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 1265cfc..47daa19 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/tidwall/gjson v1.19.0 go.mau.fi/util v0.9.9 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20260529101937-a7ea56383ec4 + go.mau.fi/whatsmeow v0.0.0-20260531220358-9ff5508a26c2 golang.org/x/image v0.40.0 golang.org/x/net v0.55.0 golang.org/x/sync v0.20.0 diff --git a/go.sum b/go.sum index b1c29ff..b4fcfd5 100644 --- a/go.sum +++ b/go.sum @@ -75,8 +75,8 @@ go.mau.fi/util v0.9.9 h1:ujDeXCo07HBor5oQLyO1tHklupmqVmPgasc53d7q/NE= go.mau.fi/util v0.9.9/go.mod h1:pqt4Vcrt+5gcH/CgrHZg11qSx+b34o6mknGzOEA6waY= 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-20260529101937-a7ea56383ec4 h1:PasY/1lMvmSeSxzXLxqgxf95pjwcnTeQ77t4ERiCbY8= -go.mau.fi/whatsmeow v0.0.0-20260529101937-a7ea56383ec4/go.mod h1:9hto2r5yVE5yyNTRrZErKNSflGBKxIplUVXAD3EJFDE= +go.mau.fi/whatsmeow v0.0.0-20260531220358-9ff5508a26c2 h1:1ymXAy8tRIYzwEL8cmXEWg3QX6XP3hDt2eqBSh2ZtRI= +go.mau.fi/whatsmeow v0.0.0-20260531220358-9ff5508a26c2/go.mod h1:9hto2r5yVE5yyNTRrZErKNSflGBKxIplUVXAD3EJFDE= 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.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= From ccd62ee30e799c0674d7138a1275c2f64152a5ea Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 2 Jun 2026 20:44:57 +0300 Subject: [PATCH 14/29] backfill: add trace log with content if message fails to parse --- go.mod | 2 +- go.sum | 4 ++-- pkg/connector/backfill.go | 6 ++++++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 47daa19..4665f82 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/tidwall/gjson v1.19.0 go.mau.fi/util v0.9.9 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20260531220358-9ff5508a26c2 + go.mau.fi/whatsmeow v0.0.0-20260602173933-595ceb0c22bf golang.org/x/image v0.40.0 golang.org/x/net v0.55.0 golang.org/x/sync v0.20.0 diff --git a/go.sum b/go.sum index b4fcfd5..b682043 100644 --- a/go.sum +++ b/go.sum @@ -75,8 +75,8 @@ go.mau.fi/util v0.9.9 h1:ujDeXCo07HBor5oQLyO1tHklupmqVmPgasc53d7q/NE= go.mau.fi/util v0.9.9/go.mod h1:pqt4Vcrt+5gcH/CgrHZg11qSx+b34o6mknGzOEA6waY= 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-20260531220358-9ff5508a26c2 h1:1ymXAy8tRIYzwEL8cmXEWg3QX6XP3hDt2eqBSh2ZtRI= -go.mau.fi/whatsmeow v0.0.0-20260531220358-9ff5508a26c2/go.mod h1:9hto2r5yVE5yyNTRrZErKNSflGBKxIplUVXAD3EJFDE= +go.mau.fi/whatsmeow v0.0.0-20260602173933-595ceb0c22bf h1:zyTgHzoTL1rPeLn8B761tnUoNTHxfhy6DsQbHNzc7/Q= +go.mau.fi/whatsmeow v0.0.0-20260602173933-595ceb0c22bf/go.mod h1:9hto2r5yVE5yyNTRrZErKNSflGBKxIplUVXAD3EJFDE= 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.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= diff --git a/pkg/connector/backfill.go b/pkg/connector/backfill.go index b4ef181..0e6de76 100644 --- a/pkg/connector/backfill.go +++ b/pkg/connector/backfill.go @@ -260,6 +260,9 @@ func (wa *WhatsAppClient) handleWAHistorySync( Str("msg_id", rawMsg.GetMessage().GetKey().GetID()). Uint64("msg_time_seconds", rawMsg.GetMessage().GetMessageTimestamp()). Msg("Dropping historical message due to parse error") + log.Trace(). + Any("web_message_info", rawMsg.GetMessage()). + Msg("Content of event that failed to parse") continue } if firstItemTime.IsZero() { @@ -603,6 +606,9 @@ func (wa *WhatsAppClient) convertHistorySyncMessages( Str("msg_id", msg.GetKey().GetID()). Uint64("msg_time_seconds", msg.GetMessageTimestamp()). Msg("Dropping historical message due to parse error") + zerolog.Ctx(ctx).Trace(). + Any("web_message_info", msg.GetMessage()). + Msg("Content of event that failed to parse") continue } if !explodeOnError { From e4f84014e7921783f34f5d88f2a2b8a8bf23c8a9 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 3 Jun 2026 16:32:17 +0300 Subject: [PATCH 15/29] 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 4665f82..4efd3b8 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/tidwall/gjson v1.19.0 go.mau.fi/util v0.9.9 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20260602173933-595ceb0c22bf + go.mau.fi/whatsmeow v0.0.0-20260603132417-6a7ac9915382 golang.org/x/image v0.40.0 golang.org/x/net v0.55.0 golang.org/x/sync v0.20.0 diff --git a/go.sum b/go.sum index b682043..764681f 100644 --- a/go.sum +++ b/go.sum @@ -75,8 +75,8 @@ go.mau.fi/util v0.9.9 h1:ujDeXCo07HBor5oQLyO1tHklupmqVmPgasc53d7q/NE= go.mau.fi/util v0.9.9/go.mod h1:pqt4Vcrt+5gcH/CgrHZg11qSx+b34o6mknGzOEA6waY= 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-20260602173933-595ceb0c22bf h1:zyTgHzoTL1rPeLn8B761tnUoNTHxfhy6DsQbHNzc7/Q= -go.mau.fi/whatsmeow v0.0.0-20260602173933-595ceb0c22bf/go.mod h1:9hto2r5yVE5yyNTRrZErKNSflGBKxIplUVXAD3EJFDE= +go.mau.fi/whatsmeow v0.0.0-20260603132417-6a7ac9915382 h1:j/iSrXBAN0bmQRJysLdnE0hMAqO2ttQTsovO/0nLar4= +go.mau.fi/whatsmeow v0.0.0-20260603132417-6a7ac9915382/go.mod h1:9hto2r5yVE5yyNTRrZErKNSflGBKxIplUVXAD3EJFDE= 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.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= From d9baddaae1f8df98288899038e852f08d2964ee7 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 4 Jun 2026 23:58:09 +0300 Subject: [PATCH 16/29] dependencies: update whatsmeow --- go.mod | 2 +- go.sum | 4 ++-- pkg/connector/chatinfo.go | 6 +++--- pkg/connector/directmedia.go | 6 ++---- pkg/connector/userinfo.go | 2 +- pkg/msgconv/wa-media.go | 4 ++-- 6 files changed, 11 insertions(+), 13 deletions(-) diff --git a/go.mod b/go.mod index 4efd3b8..ae10fd5 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/tidwall/gjson v1.19.0 go.mau.fi/util v0.9.9 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20260603132417-6a7ac9915382 + go.mau.fi/whatsmeow v0.0.0-20260604205742-c6a4b703e48f golang.org/x/image v0.40.0 golang.org/x/net v0.55.0 golang.org/x/sync v0.20.0 diff --git a/go.sum b/go.sum index 764681f..29beb99 100644 --- a/go.sum +++ b/go.sum @@ -75,8 +75,8 @@ go.mau.fi/util v0.9.9 h1:ujDeXCo07HBor5oQLyO1tHklupmqVmPgasc53d7q/NE= go.mau.fi/util v0.9.9/go.mod h1:pqt4Vcrt+5gcH/CgrHZg11qSx+b34o6mknGzOEA6waY= 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-20260603132417-6a7ac9915382 h1:j/iSrXBAN0bmQRJysLdnE0hMAqO2ttQTsovO/0nLar4= -go.mau.fi/whatsmeow v0.0.0-20260603132417-6a7ac9915382/go.mod h1:9hto2r5yVE5yyNTRrZErKNSflGBKxIplUVXAD3EJFDE= +go.mau.fi/whatsmeow v0.0.0-20260604205742-c6a4b703e48f h1:7JxeZa++EnM3z+94NL+gxnDKWLYyml4W7x2dMMYGUNQ= +go.mau.fi/whatsmeow v0.0.0-20260604205742-c6a4b703e48f/go.mod h1:9hto2r5yVE5yyNTRrZErKNSflGBKxIplUVXAD3EJFDE= 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.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= diff --git a/pkg/connector/chatinfo.go b/pkg/connector/chatinfo.go index 7c7dc8e..7ffbb14 100644 --- a/pkg/connector/chatinfo.go +++ b/pkg/connector/chatinfo.go @@ -457,7 +457,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(ctx, avatar.DirectPath, nil, nil, nil, 0, "", "") + return wa.Client.DownloadMediaWithOnlyPath(ctx, avatar.DirectPath) }, } } @@ -496,7 +496,7 @@ func (wa *WhatsAppClient) wrapNewsletterInfo(ctx context.Context, info *types.Ne 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(ctx, info.ThreadMeta.Picture.DirectPath, nil, nil, nil, 0, "", "") + return wa.Client.DownloadMediaWithOnlyPath(ctx, info.ThreadMeta.Picture.DirectPath) } } else if info.ThreadMeta.Preview.ID != "" { avatar.ID = networkid.AvatarID(info.ThreadMeta.Preview.ID) @@ -507,7 +507,7 @@ func (wa *WhatsAppClient) wrapNewsletterInfo(ctx context.Context, info *types.Ne } else if meta.ThreadMeta.Picture == nil { return nil, fmt.Errorf("full res avatar info is missing") } - return wa.Client.DownloadMediaWithPath(ctx, meta.ThreadMeta.Picture.DirectPath, nil, nil, nil, 0, "", "") + return wa.Client.DownloadMediaWithOnlyPath(ctx, meta.ThreadMeta.Picture.DirectPath) } } else { avatar.ID = "remove" diff --git a/pkg/connector/directmedia.go b/pkg/connector/directmedia.go index 70289cd..89a54fc 100644 --- a/pkg/connector/directmedia.go +++ b/pkg/connector/directmedia.go @@ -131,9 +131,7 @@ func (wa *WhatsAppConnector) downloadAvatarDirectMedia(ctx context.Context, pars } return &mediaproxy.GetMediaResponseFile{ Callback: func(w *os.File) (*mediaproxy.FileMeta, error) { - return &mediaproxy.FileMeta{}, waClient.Client.DownloadMediaWithPathToFile( - ctx, cachedInfo.DirectPath, nil, nil, nil, 0, "", "", w, - ) + return &mediaproxy.FileMeta{}, waClient.Client.DownloadMediaWithOnlyPathToFile(ctx, cachedInfo.DirectPath, w) }, }, nil } @@ -227,7 +225,7 @@ func (wa *WhatsAppConnector) makeDirectMediaResponse( log.Trace().Msg("Retrying download after successful retry") err = waClient.Client.DownloadToFile(ctx, keys, f) } - if errors.Is(err, whatsmeow.ErrFileLengthMismatch) || errors.Is(err, whatsmeow.ErrInvalidMediaSHA256) { + if 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 nil, err diff --git a/pkg/connector/userinfo.go b/pkg/connector/userinfo.go index aa84938..2b94047 100644 --- a/pkg/connector/userinfo.go +++ b/pkg/connector/userinfo.go @@ -357,7 +357,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(ctx, avatar.DirectPath, nil, nil, nil, 0, "", "") + return wa.Client.DownloadMediaWithOnlyPath(ctx, avatar.DirectPath) }, } } diff --git a/pkg/msgconv/wa-media.go b/pkg/msgconv/wa-media.go index 38fe9e2..34c529b 100644 --- a/pkg/msgconv/wa-media.go +++ b/pkg/msgconv/wa-media.go @@ -374,7 +374,7 @@ func (mc *MessageConverter) reuploadWhatsAppAttachment( var err 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) { + if 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 nil, fmt.Errorf("%w: %w", bridgev2.ErrMediaDownloadFailed, err) @@ -395,7 +395,7 @@ func (mc *MessageConverter) reuploadWhatsAppAttachment( } } else { data, err := client.Download(ctx, message) - if errors.Is(err, whatsmeow.ErrFileLengthMismatch) || errors.Is(err, whatsmeow.ErrInvalidMediaSHA256) { + if 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 fmt.Errorf("%w: %w", bridgev2.ErrMediaDownloadFailed, err) From 5e13abdc30419c73250d90f242f5c3ce54be7050 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 9 Jun 2026 12:25:24 +0300 Subject: [PATCH 17/29] dependencies: update --- go.mod | 20 ++++++++++---------- go.sum | 36 ++++++++++++++++++------------------ 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/go.mod b/go.mod index ae10fd5..f37b568 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module go.mau.fi/mautrix-whatsapp go 1.25.0 -toolchain go1.26.3 +toolchain go1.26.4 tool go.mau.fi/util/cmd/maubuild @@ -12,13 +12,13 @@ require ( github.com/tidwall/gjson v1.19.0 go.mau.fi/util v0.9.9 go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20260604205742-c6a4b703e48f - golang.org/x/image v0.40.0 + go.mau.fi/whatsmeow v0.0.0-20260609091626-4e622162b959 + golang.org/x/image v0.42.0 golang.org/x/net v0.55.0 - golang.org/x/sync v0.20.0 + golang.org/x/sync v0.21.0 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.28.1-0.20260527103108-d00b9b09fcd5 + maunium.net/go/mautrix v0.28.1-0.20260609092200-dbbc3d0ed084 ) 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.44 // indirect + github.com/mattn/go-sqlite3 v1.14.45 // 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 @@ -43,11 +43,11 @@ require ( github.com/yuin/goldmark v1.8.2 // indirect go.mau.fi/libsignal v0.2.2 // indirect go.mau.fi/zeroconfig v0.2.0 // indirect - golang.org/x/crypto v0.52.0 // indirect - golang.org/x/exp v0.0.0-20260508232706-74f9aab9d74a // indirect + golang.org/x/crypto v0.53.0 // indirect + golang.org/x/exp v0.0.0-20260603202125-055de637280b // indirect golang.org/x/mod v0.36.0 // indirect - golang.org/x/sys v0.45.0 // indirect - golang.org/x/text v0.37.0 // indirect + golang.org/x/sys v0.46.0 // indirect + golang.org/x/text v0.38.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 29beb99..589125f 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.44 h1:3VSe+xafpbzsLbdr2AWlAZk9yRHiBhTBakioXaCKTF8= -github.com/mattn/go-sqlite3 v1.14.44/go.mod h1:pjEuOr8IwzLJP2MfGeTb0A35jauH+C2kbHKBr7yXKVQ= +github.com/mattn/go-sqlite3 v1.14.45 h1:6KA/spDguL3KV8rnybG7ezSaE4SeMR3KC9VbUoAQaIk= +github.com/mattn/go-sqlite3 v1.14.45/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= @@ -75,27 +75,27 @@ go.mau.fi/util v0.9.9 h1:ujDeXCo07HBor5oQLyO1tHklupmqVmPgasc53d7q/NE= go.mau.fi/util v0.9.9/go.mod h1:pqt4Vcrt+5gcH/CgrHZg11qSx+b34o6mknGzOEA6waY= 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-20260604205742-c6a4b703e48f h1:7JxeZa++EnM3z+94NL+gxnDKWLYyml4W7x2dMMYGUNQ= -go.mau.fi/whatsmeow v0.0.0-20260604205742-c6a4b703e48f/go.mod h1:9hto2r5yVE5yyNTRrZErKNSflGBKxIplUVXAD3EJFDE= +go.mau.fi/whatsmeow v0.0.0-20260609091626-4e622162b959 h1:5MpMyxG2lGLgnN0zKfD6fnDBvyGXoOlruLK34tV281w= +go.mau.fi/whatsmeow v0.0.0-20260609091626-4e622162b959/go.mod h1:9hto2r5yVE5yyNTRrZErKNSflGBKxIplUVXAD3EJFDE= 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.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= -golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= -golang.org/x/exp v0.0.0-20260508232706-74f9aab9d74a h1:+3jdDGGB8NGb1Zktc737jlt3/A5f6UlwSzmvqUuufxw= -golang.org/x/exp v0.0.0-20260508232706-74f9aab9d74a/go.mod h1:d2fgXJLVs4dYDHUk5lwMIfzRzSrWCfGZb0ZqeLa/Vcw= -golang.org/x/image v0.40.0 h1:Tw4GyDXMo+daZN1znreBRC3VayR1aLFUyUEOLUdW1a8= -golang.org/x/image v0.40.0/go.mod h1:uIc348UZMSvS5Z65CVZ7iDPaNobNFEPeJ4kbqTOszmA= +golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto= +golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio= +golang.org/x/exp v0.0.0-20260603202125-055de637280b h1:v1uXiEBHo8QA0LiGCo7UgHMzHT4Kdfpl2zmtH5vaP1Q= +golang.org/x/exp v0.0.0-20260603202125-055de637280b/go.mod h1:d2fgXJLVs4dYDHUk5lwMIfzRzSrWCfGZb0ZqeLa/Vcw= +golang.org/x/image v0.42.0 h1:1gSs6ehNWXLbkHBIPcWztk3D/6aIA/8hauiAYtlodVY= +golang.org/x/image v0.42.0/go.mod h1:rrpelvGFt+kLPAjPM4HeWPgrl0FtafueU//e5N0qk/Q= golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4= golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ= golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= -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/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM= +golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= -golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= -golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= +golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw= +golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE= +golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4= 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.28.1-0.20260527103108-d00b9b09fcd5 h1:9bx9er6TJxQ2saQw63gwT0HZWg9/0Q30x8V3foKuNBo= -maunium.net/go/mautrix v0.28.1-0.20260527103108-d00b9b09fcd5/go.mod h1:/a9A7LGaqb9B3nho4tLd28n0EPcCdwpm2dxkxkLLgh0= +maunium.net/go/mautrix v0.28.1-0.20260609092200-dbbc3d0ed084 h1:eYPasZ1KwR+8p4nOmafIX4ZAu25lY4ouN99m64qg8iA= +maunium.net/go/mautrix v0.28.1-0.20260609092200-dbbc3d0ed084/go.mod h1:5rE5/8dcwa7V6BpJ28TDNP676GQHHiTkBtACPbwtdTE= From 1d68051dae9337a3466de6409b0c8ec86ab73807 Mon Sep 17 00:00:00 2001 From: Kishan Bagaria <1093313+KishanBagaria@users.noreply.github.com> Date: Tue, 9 Jun 2026 15:25:21 +0530 Subject: [PATCH 18/29] handlewhatsapp: fix HD media caption edits (#927) --- pkg/connector/events.go | 7 ++++++- pkg/connector/handlewhatsapp.go | 19 ++++++++++++------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/pkg/connector/events.go b/pkg/connector/events.go index 3e809b7..411c39e 100644 --- a/pkg/connector/events.go +++ b/pkg/connector/events.go @@ -182,12 +182,15 @@ func (evt *WAMessageEvent) ConvertEdit(ctx context.Context, portal *bridgev2.Por } var editedMsg *waE2E.Message var previouslyConvertedPart *bridgev2.ConvertedMessagePart + targetMessage := evt.GetTargetMessage() + cacheMessage := targetMessage if evt.isUndecryptableUpsertSubEvent { // TODO db metadata needs to be updated in this case to remove the error editedMsg = evt.Message + cacheMessage = evt.GetID() } else { editedMsg = evt.Message.GetProtocolMessage().GetEditedMessage() - previouslyConvertedPart = evt.wa.Main.GetMediaEditCache(portal, evt.GetTargetMessage()) + previouslyConvertedPart = evt.wa.Main.GetMediaEditCache(portal, targetMessage) meta := existing[0].Metadata.(*waid.MessageMetadata) if slices.Contains(meta.Edits, evt.Info.ID) { return nil, fmt.Errorf("%w: edit already handled", bridgev2.ErrIgnoringRemoteEvent) @@ -203,6 +206,8 @@ func (evt *WAMessageEvent) ConvertEdit(ctx context.Context, portal *bridgev2.Por evt.postHandle = func() { evt.wa.processFailedMedia(ctx, portal.PortalKey, evt.GetID(), cm, false) } + } else if len(cm.Parts) > 0 && cacheMessage != "" { + evt.wa.Main.AddMediaEditCache(portal, cacheMessage, cm.Parts[0]) } editPart := cm.Parts[0].ToEditPart(existing[0]) if evt.isUndecryptableUpsertSubEvent || evt.dontRenderEdited { diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index 79871b3..e6437ec 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -363,23 +363,28 @@ func (wa *WhatsAppClient) handleWAMessage(ctx context.Context, evt *events.Messa 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() + protocolMsg := evt.Message.GetProtocolMessage() + if protocolMsg.GetType() != waE2E.ProtocolMessage_MESSAGE_EDIT || protocolMsg.GetKey() == nil { + protocolMsg = &waE2E.ProtocolMessage{ + Type: waE2E.ProtocolMessage_MESSAGE_EDIT.Enum(), + Key: parentKey, + EditedMessage: evt.Message.GetAssociatedChildMessage().GetMessage(), + } + dontRenderEdited = true + } else if child := protocolMsg.GetEditedMessage().GetAssociatedChildMessage().GetMessage(); child != nil { + protocolMsg.EditedMessage = child + protocolMsg.Key = parentKey + } 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) - dontRenderEdited = true } else if assocType == waE2E.MessageAssociation_MOTION_PHOTO { //evt.Message = evt.Message.GetAssociatedChildMessage().GetMessage() wa.UserLogin.Log.Debug(). From 3cd66ef74a198545a0ba64c869c2e28833d75e66 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sat, 13 Jun 2026 18:37:41 +0300 Subject: [PATCH 19/29] legacyprovision: remove last parts --- cmd/mautrix-whatsapp/legacyprovision.go | 153 ------------------------ cmd/mautrix-whatsapp/main.go | 9 -- 2 files changed, 162 deletions(-) delete mode 100644 cmd/mautrix-whatsapp/legacyprovision.go diff --git a/cmd/mautrix-whatsapp/legacyprovision.go b/cmd/mautrix-whatsapp/legacyprovision.go deleted file mode 100644 index f0527b2..0000000 --- a/cmd/mautrix-whatsapp/legacyprovision.go +++ /dev/null @@ -1,153 +0,0 @@ -package main - -import ( - "net/http" - "strings" - - "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" - "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).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", - 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, - }, - }) -} - -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) - } -} - -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 cb1632c..1da86bf 100644 --- a/cmd/mautrix-whatsapp/main.go +++ b/cmd/mautrix-whatsapp/main.go @@ -24,15 +24,6 @@ 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.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) m.Run() } From c97a1c4b4dcc994464745cc7fa232082c28912b5 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sat, 13 Jun 2026 21:32:17 +0300 Subject: [PATCH 20/29] mclient: add more hooks --- pkg/connector/handlematrix.go | 55 ++++++++++++++++++++++----------- pkg/connector/handlewhatsapp.go | 3 +- pkg/connector/mclient.go | 11 +++++-- 3 files changed, 47 insertions(+), 22 deletions(-) diff --git a/pkg/connector/handlematrix.go b/pkg/connector/handlematrix.go index b0963ec..299449f 100644 --- a/pkg/connector/handlematrix.go +++ b/pkg/connector/handlematrix.go @@ -48,11 +48,12 @@ var ( _ bridgev2.DeleteChatHandlingNetworkAPI = (*WhatsAppClient)(nil) ) -func (wa *WhatsAppClient) HandleMatrixPollStart(ctx context.Context, msg *bridgev2.MatrixPollStart) (*bridgev2.MatrixMessageResponse, error) { +func (wa *WhatsAppClient) HandleMatrixPollStart(ctx context.Context, msg *bridgev2.MatrixPollStart) (result *bridgev2.MatrixMessageResponse, retErr error) { waMsg, optionMap, err := wa.Main.MsgConv.PollStartToWhatsApp(ctx, msg.Content, msg.ReplyTo, msg.Portal) if err != nil { return nil, fmt.Errorf("failed to convert poll vote: %w", err) } + defer wa.mcTrack(msg, time.Now(), &retErr) resp, err := wa.handleConvertedMatrixMessage(ctx, &msg.MatrixMessage, waMsg, nil) if err != nil { return nil, err @@ -67,19 +68,21 @@ func (wa *WhatsAppClient) HandleMatrixPollStart(ctx context.Context, msg *bridge return resp, nil } -func (wa *WhatsAppClient) HandleMatrixPollVote(ctx context.Context, msg *bridgev2.MatrixPollVote) (*bridgev2.MatrixMessageResponse, error) { +func (wa *WhatsAppClient) HandleMatrixPollVote(ctx context.Context, msg *bridgev2.MatrixPollVote) (result *bridgev2.MatrixMessageResponse, retErr error) { waMsg, err := wa.Main.MsgConv.PollVoteToWhatsApp(ctx, wa.Client, msg.Content, msg.VoteTo) if err != nil { return nil, fmt.Errorf("failed to convert poll vote: %w", err) } + defer wa.mcTrack(msg, time.Now(), &retErr) return wa.handleConvertedMatrixMessage(ctx, &msg.MatrixMessage, waMsg, nil) } -func (wa *WhatsAppClient) HandleMatrixMessage(ctx context.Context, msg *bridgev2.MatrixMessage) (*bridgev2.MatrixMessageResponse, error) { +func (wa *WhatsAppClient) HandleMatrixMessage(ctx context.Context, msg *bridgev2.MatrixMessage) (result *bridgev2.MatrixMessageResponse, retErr error) { waMsg, req, err := wa.Main.MsgConv.ToWhatsApp(ctx, wa.Client, msg.Event, msg.Content, msg.ReplyTo, msg.ThreadRoot, msg.Portal) if err != nil { return nil, fmt.Errorf("failed to convert message: %w", err) } + defer wa.mcTrack(msg, time.Now(), &retErr) return wa.handleConvertedMatrixMessage(ctx, msg, waMsg, req) } @@ -154,7 +157,7 @@ func (wa *WhatsAppClient) PreHandleMatrixReaction(_ context.Context, msg *bridge }, nil } -func (wa *WhatsAppClient) HandleMatrixReaction(ctx context.Context, msg *bridgev2.MatrixReaction) (*database.Reaction, error) { +func (wa *WhatsAppClient) HandleMatrixReaction(ctx context.Context, msg *bridgev2.MatrixReaction) (result *database.Reaction, retErr error) { messageID, err := waid.ParseMessageID(msg.TargetMessage.ID) if err != nil { return nil, fmt.Errorf("failed to parse target message ID: %w", err) @@ -171,6 +174,7 @@ func (wa *WhatsAppClient) HandleMatrixReaction(ctx context.Context, msg *bridgev SenderTimestampMS: proto.Int64(msg.Event.Timestamp), }, } + defer wa.mcTrack(msg, time.Now(), &retErr) var req whatsmeow.SendRequestExtra if msg.Portal.Metadata.(*waid.PortalMetadata).CommunityAnnouncementGroup { reactionMsg.EncReactionMessage, err = wa.Client.EncryptReaction(ctx, msgconv.MessageIDToInfo(wa.Client, messageID), reactionMsg.ReactionMessage) @@ -192,7 +196,7 @@ func (wa *WhatsAppClient) HandleMatrixReaction(ctx context.Context, msg *bridgev }, err } -func (wa *WhatsAppClient) HandleMatrixReactionRemove(ctx context.Context, msg *bridgev2.MatrixReactionRemove) error { +func (wa *WhatsAppClient) HandleMatrixReactionRemove(ctx context.Context, msg *bridgev2.MatrixReactionRemove) (retErr error) { messageID, err := waid.ParseMessageID(msg.TargetReaction.MessageID) if err != nil { return fmt.Errorf("failed to parse target message ID: %w", err) @@ -216,12 +220,13 @@ func (wa *WhatsAppClient) HandleMatrixReactionRemove(ctx context.Context, msg *b extra.ID = types.MessageID(msg.InputTransactionID) } + defer wa.mcTrack(msg, time.Now(), &retErr) resp, err := wa.Client.SendMessage(ctx, portalJID, reactionMsg, extra) zerolog.Ctx(ctx).Trace().Any("response", resp).Msg("WhatsApp reaction response") return err } -func (wa *WhatsAppClient) HandleMatrixEdit(ctx context.Context, edit *bridgev2.MatrixEdit) error { +func (wa *WhatsAppClient) HandleMatrixEdit(ctx context.Context, edit *bridgev2.MatrixEdit) (retErr error) { log := zerolog.Ctx(ctx) var editID types.MessageID @@ -245,6 +250,8 @@ func (wa *WhatsAppClient) HandleMatrixEdit(ctx context.Context, edit *bridgev2.M if err != nil { return fmt.Errorf("failed to convert message: %w", err) } + defer wa.mcTrack(edit, time.Now(), &retErr) + convertedEdit := wa.Client.BuildEdit(messageID.Chat, messageID.ID, waMsg) if edit.OrigSender == nil { convertedEdit.EditedMessage.Message.ProtocolMessage.TimestampMS = proto.Int64(edit.Event.Timestamp) @@ -259,7 +266,7 @@ func (wa *WhatsAppClient) HandleMatrixEdit(ctx context.Context, edit *bridgev2.M return err } -func (wa *WhatsAppClient) HandleMatrixMessageRemove(ctx context.Context, msg *bridgev2.MatrixMessageRemove) error { +func (wa *WhatsAppClient) HandleMatrixMessageRemove(ctx context.Context, msg *bridgev2.MatrixMessageRemove) (retErr error) { log := zerolog.Ctx(ctx) messageID, err := waid.ParseMessageID(msg.TargetMessage.ID) if err != nil { @@ -271,6 +278,7 @@ func (wa *WhatsAppClient) HandleMatrixMessageRemove(ctx context.Context, msg *br return fmt.Errorf("failed to parse portal ID: %w", err) } + defer wa.mcTrack(msg, time.Now(), &retErr) revokeMessage := wa.Client.BuildRevoke(messageID.Chat, messageID.Sender, messageID.ID) extra := whatsmeow.SendRequestExtra{} @@ -283,7 +291,7 @@ func (wa *WhatsAppClient) HandleMatrixMessageRemove(ctx context.Context, msg *br return err } -func (wa *WhatsAppClient) HandleMatrixReadReceipt(ctx context.Context, receipt *bridgev2.MatrixReadReceipt) error { +func (wa *WhatsAppClient) HandleMatrixReadReceipt(ctx context.Context, receipt *bridgev2.MatrixReadReceipt) (retErr error) { if !receipt.ReadUpTo.After(receipt.LastRead) { return nil } @@ -322,6 +330,7 @@ func (wa *WhatsAppClient) HandleMatrixReadReceipt(ctx context.Context, receipt * } messagesToRead[key] = append(messagesToRead[key], parsed.ID) } + defer wa.mcTrack(receipt, time.Now(), &retErr) for messageSender, ids := range messagesToRead { err = wa.Client.MarkRead(ctx, ids, receipt.Receipt.Timestamp, portalJID, messageSender) if err != nil { @@ -331,7 +340,7 @@ func (wa *WhatsAppClient) HandleMatrixReadReceipt(ctx context.Context, receipt * return err } -func (wa *WhatsAppClient) HandleMatrixTyping(ctx context.Context, msg *bridgev2.MatrixTyping) error { +func (wa *WhatsAppClient) HandleMatrixTyping(ctx context.Context, msg *bridgev2.MatrixTyping) (retErr error) { portalJID, err := waid.ParsePortalID(msg.Portal.ID) if err != nil { return err @@ -352,6 +361,7 @@ func (wa *WhatsAppClient) HandleMatrixTyping(ctx context.Context, msg *bridgev2. return nil } + defer wa.mcTrack(msg, time.Now(), &retErr) if wa.Main.Config.SendPresenceOnTyping { err = wa.updatePresence(ctx, types.PresenceAvailable) if err != nil { @@ -363,7 +373,7 @@ func (wa *WhatsAppClient) HandleMatrixTyping(ctx context.Context, msg *bridgev2. 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) { +func (wa *WhatsAppClient) HandleMatrixDisappearingTimer(ctx context.Context, msg *bridgev2.MatrixDisappearingTimer) (ok bool, retErr error) { portalJID, err := waid.ParsePortalID(msg.Portal.ID) if err != nil { return false, err @@ -375,6 +385,7 @@ func (wa *WhatsAppClient) HandleMatrixDisappearingTimer(ctx context.Context, msg return false, fmt.Errorf("%w (%s)", errUnsupportedDisappearingTimer, msg.Content.Timer.Duration) } + defer wa.mcTrack(msg, time.Now(), &retErr) settingTS := time.UnixMilli(msg.Event.Timestamp) err = wa.Client.SetDisappearingTimer(ctx, portalJID, msg.Content.Timer.Duration, settingTS) if err != nil { @@ -391,7 +402,7 @@ func (wa *WhatsAppClient) HandleMatrixDisappearingTimer(ctx context.Context, msg return true, nil } -func (wa *WhatsAppClient) HandleMatrixMembership(ctx context.Context, msg *bridgev2.MatrixMembershipChange) (*bridgev2.MatrixMembershipResult, error) { +func (wa *WhatsAppClient) HandleMatrixMembership(ctx context.Context, msg *bridgev2.MatrixMembershipChange) (result *bridgev2.MatrixMembershipResult, retErr error) { if msg.Type.IsSelf && msg.OrigSender != nil { return nil, nil } @@ -435,6 +446,7 @@ func (wa *WhatsAppClient) HandleMatrixMembership(ctx context.Context, msg *bridg return nil, fmt.Errorf("cannot get target intent: unknown type: %T", target) } + defer wa.mcTrack(msg, time.Now(), &retErr) resp, err := wa.Client.UpdateGroupParticipants(ctx, portalJID, changes, action) if err != nil { return nil, err @@ -450,7 +462,7 @@ func (wa *WhatsAppClient) HandleMatrixMembership(ctx context.Context, msg *bridg return &bridgev2.MatrixMembershipResult{RedirectTo: waid.MakeUserID(resp[0].JID)}, nil } -func (wa *WhatsAppClient) HandleMatrixRoomName(ctx context.Context, msg *bridgev2.MatrixRoomName) (bool, error) { +func (wa *WhatsAppClient) HandleMatrixRoomName(ctx context.Context, msg *bridgev2.MatrixRoomName) (ok bool, retErr error) { portalJID, err := waid.ParsePortalID(msg.Portal.ID) if err != nil { return false, err @@ -460,6 +472,7 @@ func (wa *WhatsAppClient) HandleMatrixRoomName(ctx context.Context, msg *bridgev return false, fmt.Errorf("cannot set room name for DM") } + defer wa.mcTrack(msg, time.Now(), &retErr) err = wa.Client.SetGroupName(ctx, portalJID, msg.Content.Name) if err != nil { return false, err @@ -471,7 +484,7 @@ func (wa *WhatsAppClient) HandleMatrixRoomName(ctx context.Context, msg *bridgev return true, nil } -func (wa *WhatsAppClient) HandleMatrixRoomTopic(ctx context.Context, msg *bridgev2.MatrixRoomTopic) (bool, error) { +func (wa *WhatsAppClient) HandleMatrixRoomTopic(ctx context.Context, msg *bridgev2.MatrixRoomTopic) (ok bool, retErr error) { portalJID, err := waid.ParsePortalID(msg.Portal.ID) if err != nil { return false, err @@ -481,6 +494,7 @@ func (wa *WhatsAppClient) HandleMatrixRoomTopic(ctx context.Context, msg *bridge return false, fmt.Errorf("cannot set room topic for DM") } + defer wa.mcTrack(msg, time.Now(), &retErr) newID := wa.Client.GenerateMessageID() oldID := msg.Portal.Metadata.(*waid.PortalMetadata).TopicID err = wa.Client.SetGroupTopic(ctx, portalJID, oldID, newID, msg.Content.Topic) @@ -495,7 +509,7 @@ func (wa *WhatsAppClient) HandleMatrixRoomTopic(ctx context.Context, msg *bridge return true, nil } -func (wa *WhatsAppClient) HandleMatrixRoomAvatar(ctx context.Context, msg *bridgev2.MatrixRoomAvatar) (bool, error) { +func (wa *WhatsAppClient) HandleMatrixRoomAvatar(ctx context.Context, msg *bridgev2.MatrixRoomAvatar) (ok bool, retErr error) { portalJID, err := waid.ParsePortalID(msg.Portal.ID) if err != nil { return false, err @@ -518,6 +532,7 @@ func (wa *WhatsAppClient) HandleMatrixRoomAvatar(ctx context.Context, msg *bridg } } + defer wa.mcTrack(msg, time.Now(), &retErr) avatarID, err := wa.Client.SetGroupPhoto(ctx, portalJID, data) if err != nil { return false, err @@ -585,7 +600,7 @@ func convertRoomAvatar(data []byte) ([]byte, error) { return buf.Bytes(), nil } -func (wa *WhatsAppClient) HandleMute(ctx context.Context, msg *bridgev2.MatrixMute) error { +func (wa *WhatsAppClient) HandleMute(ctx context.Context, msg *bridgev2.MatrixMute) (retErr error) { chatJID, err := waid.ParsePortalID(msg.Portal.ID) if err != nil { return err @@ -596,14 +611,16 @@ func (wa *WhatsAppClient) HandleMute(ctx context.Context, msg *bridgev2.MatrixMu if !muted || mutedUntil == event.MutedForever { muteTS = nil } + defer wa.mcTrack(msg, time.Now(), &retErr) return wa.Client.SendAppState(ctx, appstate.BuildMuteAbs(chatJID, muted, muteTS)) } -func (wa *WhatsAppClient) HandleRoomTag(ctx context.Context, msg *bridgev2.MatrixRoomTag) error { +func (wa *WhatsAppClient) HandleRoomTag(ctx context.Context, msg *bridgev2.MatrixRoomTag) (retErr error) { chatJID, err := waid.ParsePortalID(msg.Portal.ID) if err != nil { return err } + defer wa.mcTrack(msg, time.Now(), &retErr) _, isFavorite := msg.Content.Tags[event.RoomTagFavourite] return wa.Client.SendAppState(ctx, appstate.BuildPin(chatJID, isFavorite)) } @@ -635,7 +652,7 @@ func (wa *WhatsAppClient) getLastMessageInfo(ctx context.Context, chatJID types. return lastTS, lastKey, nil } -func (wa *WhatsAppClient) HandleMarkedUnread(ctx context.Context, msg *bridgev2.MatrixMarkedUnread) error { +func (wa *WhatsAppClient) HandleMarkedUnread(ctx context.Context, msg *bridgev2.MatrixMarkedUnread) (retErr error) { chatJID, err := waid.ParsePortalID(msg.Portal.ID) if err != nil { return err @@ -644,10 +661,11 @@ func (wa *WhatsAppClient) HandleMarkedUnread(ctx context.Context, msg *bridgev2. if err != nil { return err } + defer wa.mcTrack(msg, time.Now(), &retErr) return wa.Client.SendAppState(ctx, appstate.BuildMarkChatAsRead(chatJID, msg.Content.Unread, lastTS, lastKey)) } -func (wa *WhatsAppClient) HandleMatrixDeleteChat(ctx context.Context, msg *bridgev2.MatrixDeleteChat) error { +func (wa *WhatsAppClient) HandleMatrixDeleteChat(ctx context.Context, msg *bridgev2.MatrixDeleteChat) (retErr error) { chatJID, err := waid.ParsePortalID(msg.Portal.ID) if err != nil { return err @@ -668,5 +686,6 @@ func (wa *WhatsAppClient) HandleMatrixDeleteChat(ctx context.Context, msg *bridg if err != nil { return err } + defer wa.mcTrack(msg, time.Now(), &retErr) return wa.Client.SendAppState(ctx, appstate.BuildDeleteChat(chatJID, lastTS, lastKey, true)) } diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index e6437ec..30a9cd8 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -28,7 +28,6 @@ import ( "go.mau.fi/whatsmeow" "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" @@ -77,6 +76,7 @@ func init() { func (wa *WhatsAppClient) handleWAEvent(rawEvt any) (success bool) { log := wa.UserLogin.Log ctx := log.WithContext(wa.Main.Bridge.BackgroundCtx) + wa.MC.OnWhatsAppEvent(rawEvt) success = true switch evt := rawEvt.(type) { @@ -168,7 +168,6 @@ 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 index 0930617..f85545a 100644 --- a/pkg/connector/mclient.go +++ b/pkg/connector/mclient.go @@ -21,6 +21,7 @@ import ( "encoding/json" "time" + "go.mau.fi/util/ptr" "go.mau.fi/whatsmeow" waBinary "go.mau.fi/whatsmeow/binary" "go.mau.fi/whatsmeow/types" @@ -39,14 +40,16 @@ func (wa *WhatsAppClient) initMC() { } type mClient = interface { - OnConnect(version uint32, platform string) + OnMatrixEvent(any, time.Duration, error) + OnWhatsAppEvent(any) } type noopMC struct{} var noopMCInstance mClient = &noopMC{} -func (n *noopMC) OnConnect(version uint32, platform string) {} +func (n *noopMC) OnMatrixEvent(any, time.Duration, error) {} +func (n *noopMC) OnWhatsAppEvent(any) {} type mWAClient = interface { MSend(data []byte) @@ -79,3 +82,7 @@ func (wa *WhatsAppClient) MSave(s json.RawMessage) { wa.UserLogin.Log.Err(err).Msg("Failed to save MC data") } } + +func (wa *WhatsAppClient) mcTrack(evt any, start time.Time, err *error) { + wa.MC.OnMatrixEvent(evt, time.Since(start), ptr.Val(err)) +} From f597fd91f4e53532ccb325f0033565dd7193a985 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 16 Jun 2026 15:13:03 +0300 Subject: [PATCH 21/29] Bump version to v26.06 --- CHANGELOG.md | 8 ++++++++ cmd/mautrix-whatsapp/main.go | 2 +- go.mod | 16 ++++++++-------- go.sum | 32 ++++++++++++++++---------------- 4 files changed, 33 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c0314e..560e2b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +# v26.06 + +* Added placeholder for group message history share notices. +* Fixed community spaces not being bridged properly in some cases. +* Fixed handling edits of HD media. +* Fixed duplicate message when a message from another own device is initially + undecryptable and later gets resolved. + # v26.05 * Added support for importing sticker packs from WhatsApp. diff --git a/cmd/mautrix-whatsapp/main.go b/cmd/mautrix-whatsapp/main.go index 1da86bf..07baaec 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.05", + Version: "26.06", SemCalVer: true, Connector: &connector.WhatsAppConnector{}, } diff --git a/go.mod b/go.mod index f37b568..85f5c6c 100644 --- a/go.mod +++ b/go.mod @@ -10,21 +10,21 @@ require ( github.com/lib/pq v1.12.3 github.com/rs/zerolog v1.35.1 github.com/tidwall/gjson v1.19.0 - go.mau.fi/util v0.9.9 - go.mau.fi/webp v0.2.0 - go.mau.fi/whatsmeow v0.0.0-20260609091626-4e622162b959 + go.mau.fi/util v0.9.10 + go.mau.fi/webp v0.3.0 + go.mau.fi/whatsmeow v0.0.0-20260616120636-eaa388b4e537 golang.org/x/image v0.42.0 - golang.org/x/net v0.55.0 + golang.org/x/net v0.56.0 golang.org/x/sync v0.21.0 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.28.1-0.20260609092200-dbbc3d0ed084 + maunium.net/go/mautrix v0.28.1 ) 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/coder/websocket v1.8.15 // 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 @@ -44,8 +44,8 @@ require ( go.mau.fi/libsignal v0.2.2 // indirect go.mau.fi/zeroconfig v0.2.0 // indirect golang.org/x/crypto v0.53.0 // indirect - golang.org/x/exp v0.0.0-20260603202125-055de637280b // indirect - golang.org/x/mod v0.36.0 // indirect + golang.org/x/exp v0.0.0-20260611194520-c48552f49976 // indirect + golang.org/x/mod v0.37.0 // indirect golang.org/x/sys v0.46.0 // indirect golang.org/x/text v0.38.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect diff --git a/go.sum b/go.sum index 589125f..e0a405c 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.14 h1:9L0p0iKiNOibykf283eHkKUHHrpG7f65OE3BhhO7v9g= -github.com/coder/websocket v1.8.14/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg= +github.com/coder/websocket v1.8.15 h1:6B2JPeOGlpff2Uz6vOEH1Vzpi0iUz20A+lPVhPHtNUA= +github.com/coder/websocket v1.8.15/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg= 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= @@ -71,24 +71,24 @@ 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.2 h1:QV+XdzQkm3x3aSG7FcqfGSZuFXz83pRZPBFaPygHbOU= go.mau.fi/libsignal v0.2.2/go.mod h1:CRlIQg2J8uYTfDFvNoO8/KcZjs5cey0vbc6oj/bssY0= -go.mau.fi/util v0.9.9 h1:ujDeXCo07HBor5oQLyO1tHklupmqVmPgasc53d7q/NE= -go.mau.fi/util v0.9.9/go.mod h1:pqt4Vcrt+5gcH/CgrHZg11qSx+b34o6mknGzOEA6waY= -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-20260609091626-4e622162b959 h1:5MpMyxG2lGLgnN0zKfD6fnDBvyGXoOlruLK34tV281w= -go.mau.fi/whatsmeow v0.0.0-20260609091626-4e622162b959/go.mod h1:9hto2r5yVE5yyNTRrZErKNSflGBKxIplUVXAD3EJFDE= +go.mau.fi/util v0.9.10 h1:wzvz5iDHyqDXB8vgisD4d3SzucLXNM3iNY+1O1RoHtg= +go.mau.fi/util v0.9.10/go.mod h1:YQOxySn+ZE3qSYqNxvyX7Yi3suA8YK17PS6QqBREW7A= +go.mau.fi/webp v0.3.0 h1:gVHQZtz21Ziwj+CDuklbX9mqpsnDIFKxs/BJyV7iZzA= +go.mau.fi/webp v0.3.0/go.mod h1:rlZFTev+dYxhvk+XNBP/5GcTt4gXmzAB4DU0aGUYIQo= +go.mau.fi/whatsmeow v0.0.0-20260616120636-eaa388b4e537 h1:LhxtFShA1NXJWz4yNygwtl3Eycp9XsfMqJeDdcLyKmk= +go.mau.fi/whatsmeow v0.0.0-20260616120636-eaa388b4e537/go.mod h1:9dmNTYZ/1pHjPw/bz+azBsGjAkcrZbqzMrKcvG5bJ8U= 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.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto= golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio= -golang.org/x/exp v0.0.0-20260603202125-055de637280b h1:v1uXiEBHo8QA0LiGCo7UgHMzHT4Kdfpl2zmtH5vaP1Q= -golang.org/x/exp v0.0.0-20260603202125-055de637280b/go.mod h1:d2fgXJLVs4dYDHUk5lwMIfzRzSrWCfGZb0ZqeLa/Vcw= +golang.org/x/exp v0.0.0-20260611194520-c48552f49976 h1:X8Hz2ImujgbmetVuW+w2YkyZChE3cBpZi2P158rTG9M= +golang.org/x/exp v0.0.0-20260611194520-c48552f49976/go.mod h1:vnf4pv9iKZXY58sQE1L86zmNWJ4159e1RkcWiLCkeEY= golang.org/x/image v0.42.0 h1:1gSs6ehNWXLbkHBIPcWztk3D/6aIA/8hauiAYtlodVY= golang.org/x/image v0.42.0/go.mod h1:rrpelvGFt+kLPAjPM4HeWPgrl0FtafueU//e5N0qk/Q= -golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4= -golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ= -golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= -golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= +golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ= +golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0= +golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o= +golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec= golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM= golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -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.28.1-0.20260609092200-dbbc3d0ed084 h1:eYPasZ1KwR+8p4nOmafIX4ZAu25lY4ouN99m64qg8iA= -maunium.net/go/mautrix v0.28.1-0.20260609092200-dbbc3d0ed084/go.mod h1:5rE5/8dcwa7V6BpJ28TDNP676GQHHiTkBtACPbwtdTE= +maunium.net/go/mautrix v0.28.1 h1:Hic3oDMPbLbQu1fhboTRAKZcORMjzzkjxsa+SGk60b0= +maunium.net/go/mautrix v0.28.1/go.mod h1:mWXQNmOlrq4VTDU9f1HO03BSIswdUIyyY4wUKHqwzzY= From 879fc7cb41b0adb7ce986ee50939554c36bfa6a7 Mon Sep 17 00:00:00 2001 From: Nick Mills-Barrett Date: Wed, 17 Jun 2026 14:15:56 +0100 Subject: [PATCH 22/29] backfill: skip groups that 404 on info (#929) --- pkg/connector/backfill.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/connector/backfill.go b/pkg/connector/backfill.go index 0e6de76..ee92ac2 100644 --- a/pkg/connector/backfill.go +++ b/pkg/connector/backfill.go @@ -400,8 +400,8 @@ func (wa *WhatsAppClient) createPortalsFromHistorySync(ctx context.Context) { return } wrappedInfo, err := wa.getChatInfo(ctx, conv.ChatJID, conv, true) - if errors.Is(err, whatsmeow.ErrNotInGroup) { - log.Debug().Stringer("chat_jid", conv.ChatJID). + if errors.Is(err, whatsmeow.ErrNotInGroup) || errors.Is(err, whatsmeow.ErrGroupNotFound) { + log.Debug().Err(err).Stringer("chat_jid", conv.ChatJID). Msg("Skipping creating room because the user is not a participant") //err = wa.Main.DB.Message.DeleteAllInChat(ctx, wa.UserLogin.ID, conv.ChatJID) //if err != nil { From d90c7d94d128466195945ef1a8990be8e3e7759d Mon Sep 17 00:00:00 2001 From: Kishan Bagaria <1093313+KishanBagaria@users.noreply.github.com> Date: Fri, 19 Jun 2026 03:53:06 +0900 Subject: [PATCH 23/29] msgconv/wa-media: pluralize album media counts (#933) --- pkg/msgconv/wa-media.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/msgconv/wa-media.go b/pkg/msgconv/wa-media.go index 34c529b..9964800 100644 --- a/pkg/msgconv/wa-media.go +++ b/pkg/msgconv/wa-media.go @@ -27,6 +27,7 @@ import ( "strings" "github.com/rs/zerolog" + "go.mau.fi/util/exfmt" "go.mau.fi/util/exmime" "go.mau.fi/util/exslices" "go.mau.fi/whatsmeow" @@ -129,10 +130,10 @@ func getMediaIDVersion(msg MediaMessage) []byte { 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())) + parts = append(parts, exfmt.Pluralizable("image")(int(msg.GetExpectedImageCount()))) } if msg.GetExpectedVideoCount() > 0 { - parts = append(parts, fmt.Sprintf("%d videos", msg.GetExpectedVideoCount())) + parts = append(parts, exfmt.Pluralizable("video")(int(msg.GetExpectedVideoCount()))) } var partDesc string if len(parts) > 0 { From 41ce80f58393200e9249d29e700c778302ca2a69 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 22 Jun 2026 15:46:21 +0300 Subject: [PATCH 24/29] 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 85f5c6c..791f3e7 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/tidwall/gjson v1.19.0 go.mau.fi/util v0.9.10 go.mau.fi/webp v0.3.0 - go.mau.fi/whatsmeow v0.0.0-20260616120636-eaa388b4e537 + go.mau.fi/whatsmeow v0.0.0-20260622124518-e94c58afd43b golang.org/x/image v0.42.0 golang.org/x/net v0.56.0 golang.org/x/sync v0.21.0 diff --git a/go.sum b/go.sum index e0a405c..0112e10 100644 --- a/go.sum +++ b/go.sum @@ -75,8 +75,8 @@ go.mau.fi/util v0.9.10 h1:wzvz5iDHyqDXB8vgisD4d3SzucLXNM3iNY+1O1RoHtg= go.mau.fi/util v0.9.10/go.mod h1:YQOxySn+ZE3qSYqNxvyX7Yi3suA8YK17PS6QqBREW7A= go.mau.fi/webp v0.3.0 h1:gVHQZtz21Ziwj+CDuklbX9mqpsnDIFKxs/BJyV7iZzA= go.mau.fi/webp v0.3.0/go.mod h1:rlZFTev+dYxhvk+XNBP/5GcTt4gXmzAB4DU0aGUYIQo= -go.mau.fi/whatsmeow v0.0.0-20260616120636-eaa388b4e537 h1:LhxtFShA1NXJWz4yNygwtl3Eycp9XsfMqJeDdcLyKmk= -go.mau.fi/whatsmeow v0.0.0-20260616120636-eaa388b4e537/go.mod h1:9dmNTYZ/1pHjPw/bz+azBsGjAkcrZbqzMrKcvG5bJ8U= +go.mau.fi/whatsmeow v0.0.0-20260622124518-e94c58afd43b h1:SeXzfZd3zFyHg+JEDOb5NZqMJRCRK5NXtXy+qzDJGSs= +go.mau.fi/whatsmeow v0.0.0-20260622124518-e94c58afd43b/go.mod h1:9dmNTYZ/1pHjPw/bz+azBsGjAkcrZbqzMrKcvG5bJ8U= 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.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto= From 80b2c16b3d35aace6af59dbe89413046350c6687 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 22 Jun 2026 15:46:41 +0300 Subject: [PATCH 25/29] userinfo: log last sync time when enqueuing resync --- pkg/connector/userinfo.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pkg/connector/userinfo.go b/pkg/connector/userinfo.go index 2b94047..e827955 100644 --- a/pkg/connector/userinfo.go +++ b/pkg/connector/userinfo.go @@ -29,7 +29,8 @@ 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()) { + lastSync := ghost.Metadata.(*waid.GhostMetadata).LastSync.Time + if lastSync.Add(ResyncMinInterval).After(time.Now()) { return } wa.resyncQueueLock.Lock() @@ -43,6 +44,7 @@ func (wa *WhatsAppClient) EnqueueGhostResync(ghost *bridgev2.Ghost) { wa.UserLogin.Log.Debug(). Stringer("jid", jid). Str("next_resync_in", nextResyncIn). + Time("last_ghost_resync", lastSync). Msg("Enqueued resync for ghost") } wa.resyncQueueLock.Unlock() @@ -50,7 +52,8 @@ func (wa *WhatsAppClient) EnqueueGhostResync(ghost *bridgev2.Ghost) { func (wa *WhatsAppClient) EnqueuePortalResync(portal *bridgev2.Portal, allowDM bool) { jid, _ := waid.ParsePortalID(portal.ID) - if portal.Metadata.(*waid.PortalMetadata).LastSync.Add(ResyncMinInterval).After(time.Now()) { + lastSync := portal.Metadata.(*waid.PortalMetadata).LastSync.Time + if lastSync.Add(ResyncMinInterval).After(time.Now()) { return } else if !allowDM && jid.Server != types.GroupServer { return @@ -61,6 +64,7 @@ func (wa *WhatsAppClient) EnqueuePortalResync(portal *bridgev2.Portal, allowDM b wa.UserLogin.Log.Debug(). Stringer("jid", jid). Stringer("next_resync_in", time.Until(wa.nextResync)). + Time("last_portal_resync", lastSync). Msg("Enqueued resync for portal") } wa.resyncQueueLock.Unlock() From 0c7f5233c8b4a030e396315b9f5d6cc2d598901c Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 22 Jun 2026 15:48:40 +0300 Subject: [PATCH 26/29] handlewhatsapp: send analytics for timelock event --- pkg/connector/handlewhatsapp.go | 14 ++++++++++++++ pkg/connector/phoneping.go | 7 +++++++ pkg/waid/dbmeta.go | 3 ++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index 30a9cd8..2b9b145 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -126,6 +126,20 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) (success bool) { success = wa.handleWANewsletterLeave(evt) case *events.Picture: success = wa.handleWAPictureUpdate(ctx, evt) + case *events.NotifyAccountReachoutTimelock: + wa.UserLogin.TrackAnalytics("WhatsApp Account Reachout Timelock", map[string]any{ + "enforcement_type": evt.EnforcementType, + "is_active": evt.IsActive, + "time_enforcement_ends": evt.TimeEnforcementEnds.Time, + }) + wa.UserLogin.Metadata.(*waid.UserLoginMetadata).ReachoutTimelockUntil = evt.TimeEnforcementEnds.Time + if wa.UserLogin.BridgeState.GetPrevUnsent().StateEvent == status.StateConnected { + wa.UserLogin.BridgeState.Send(status.BridgeState{StateEvent: status.StateConnected}) + } + err := wa.UserLogin.Save(ctx) + if err != nil { + log.Err(err).Msg("Failed to save user login metadata after reachout timelock update") + } case *events.AppStateSyncComplete: wa.handleWAAppStateSyncComplete(ctx, evt) diff --git a/pkg/connector/phoneping.go b/pkg/connector/phoneping.go index ce38079..71094c9 100644 --- a/pkg/connector/phoneping.go +++ b/pkg/connector/phoneping.go @@ -37,6 +37,13 @@ func (wa *WhatsAppClient) FillBridgeState(state status.BridgeState) status.Bridg state.Error = WAPhoneOffline state.UserAction = status.UserActionOpenNative } + rtu := wa.UserLogin.Metadata.(*waid.UserLoginMetadata).ReachoutTimelockUntil + if !rtu.IsZero() { + if state.Info == nil { + state.Info = make(map[string]any) + } + state.Info["reachout_timelock_until"] = rtu + } return state } diff --git a/pkg/waid/dbmeta.go b/pkg/waid/dbmeta.go index 59e9a9e..1e54426 100644 --- a/pkg/waid/dbmeta.go +++ b/pkg/waid/dbmeta.go @@ -41,7 +41,8 @@ type UserLoginMetadata struct { AppStateRecoveryAttempted map[appstate.WAPatchName]time.Time `json:"app_state_recovery_attempted,omitempty"` - HistorySyncPortalsNeedCreating bool `json:"history_sync_portals_need_creating,omitempty"` + HistorySyncPortalsNeedCreating bool `json:"history_sync_portals_need_creating,omitempty"` + ReachoutTimelockUntil time.Time `json:"reachout_timelock_until,omitempty"` MData json.RawMessage `json:"mdata,omitempty"` } From d5cf334423689ffb67b1469b4bca6b3b0e61a34a Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 1 Jul 2026 23:01:34 +0300 Subject: [PATCH 27/29] login: add support for passkeys (#936) --- go.mod | 4 +- go.sum | 8 ++-- pkg/connector/login.go | 105 ++++++++++++++++++++++++++++++++++++++--- 3 files changed, 105 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 791f3e7..c16824c 100644 --- a/go.mod +++ b/go.mod @@ -12,13 +12,13 @@ require ( github.com/tidwall/gjson v1.19.0 go.mau.fi/util v0.9.10 go.mau.fi/webp v0.3.0 - go.mau.fi/whatsmeow v0.0.0-20260622124518-e94c58afd43b + go.mau.fi/whatsmeow v0.0.0-20260630180629-b572e5bcb92b golang.org/x/image v0.42.0 golang.org/x/net v0.56.0 golang.org/x/sync v0.21.0 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.28.1 + maunium.net/go/mautrix v0.28.2-0.20260701195654-d33ead4e1d86 ) require ( diff --git a/go.sum b/go.sum index 0112e10..d38c8cb 100644 --- a/go.sum +++ b/go.sum @@ -75,8 +75,8 @@ go.mau.fi/util v0.9.10 h1:wzvz5iDHyqDXB8vgisD4d3SzucLXNM3iNY+1O1RoHtg= go.mau.fi/util v0.9.10/go.mod h1:YQOxySn+ZE3qSYqNxvyX7Yi3suA8YK17PS6QqBREW7A= go.mau.fi/webp v0.3.0 h1:gVHQZtz21Ziwj+CDuklbX9mqpsnDIFKxs/BJyV7iZzA= go.mau.fi/webp v0.3.0/go.mod h1:rlZFTev+dYxhvk+XNBP/5GcTt4gXmzAB4DU0aGUYIQo= -go.mau.fi/whatsmeow v0.0.0-20260622124518-e94c58afd43b h1:SeXzfZd3zFyHg+JEDOb5NZqMJRCRK5NXtXy+qzDJGSs= -go.mau.fi/whatsmeow v0.0.0-20260622124518-e94c58afd43b/go.mod h1:9dmNTYZ/1pHjPw/bz+azBsGjAkcrZbqzMrKcvG5bJ8U= +go.mau.fi/whatsmeow v0.0.0-20260630180629-b572e5bcb92b h1:ZUk1ErarDNpnbosXR/MeOz2gkqA4S1bh8zjaSRj7N+Y= +go.mau.fi/whatsmeow v0.0.0-20260630180629-b572e5bcb92b/go.mod h1:9dmNTYZ/1pHjPw/bz+azBsGjAkcrZbqzMrKcvG5bJ8U= 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.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto= @@ -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.28.1 h1:Hic3oDMPbLbQu1fhboTRAKZcORMjzzkjxsa+SGk60b0= -maunium.net/go/mautrix v0.28.1/go.mod h1:mWXQNmOlrq4VTDU9f1HO03BSIswdUIyyY4wUKHqwzzY= +maunium.net/go/mautrix v0.28.2-0.20260701195654-d33ead4e1d86 h1:OKNg/klCID3MoZHuhpMSQThvWOqKtbCrXC6HjlY4+QA= +maunium.net/go/mautrix v0.28.2-0.20260701195654-d33ead4e1d86/go.mod h1:mWXQNmOlrq4VTDU9f1HO03BSIswdUIyyY4wUKHqwzzY= diff --git a/pkg/connector/login.go b/pkg/connector/login.go index b70ab1a..5da419f 100644 --- a/pkg/connector/login.go +++ b/pkg/connector/login.go @@ -2,6 +2,7 @@ package connector import ( "context" + "encoding/json" "errors" "fmt" "net/http" @@ -12,6 +13,7 @@ import ( "go.mau.fi/util/exsync" "go.mau.fi/util/jsontime" "go.mau.fi/whatsmeow" + "go.mau.fi/whatsmeow/types" "go.mau.fi/whatsmeow/types/events" waLog "go.mau.fi/whatsmeow/util/log" "maunium.net/go/mautrix/bridgev2" @@ -25,6 +27,7 @@ const ( LoginStepIDQR = "fi.mau.whatsapp.login.qr" LoginStepIDPhoneNumber = "fi.mau.whatsapp.login.phone" LoginStepIDCode = "fi.mau.whatsapp.login.code" + LoginStepIDPasskey = "fi.mau.whatsapp.login.passkey" LoginStepIDComplete = "fi.mau.whatsapp.login.complete" LoginFlowIDQR = "qr" @@ -91,9 +94,11 @@ func (wa *WhatsAppConnector) CreateLogin(_ context.Context, user *bridgev2.User, Bool("phone_code", flowID == LoginFlowIDPhone). Logger(), - WaitForQRs: exsync.NewEvent(), - LoginComplete: exsync.NewEvent(), - Received515: exsync.NewEvent(), + WaitForQRs: exsync.NewEvent(), + LoginComplete: exsync.NewEvent(), + PasskeyRequest: exsync.NewEvent(), + PasskeyConfirmation: exsync.NewEvent(), + Received515: exsync.NewEvent(), }, nil } @@ -114,6 +119,11 @@ type WALogin struct { Received515 *exsync.Event PrevQRIndex atomic.Int32 + PasskeyRequest *exsync.Event + PasskeyRequestData *events.PairPasskeyRequest + PasskeyConfirmation *exsync.Event + PasskeyConfirmationData *events.PairPasskeyConfirmation + Closed atomic.Bool EventHandlerID uint32 } @@ -122,6 +132,7 @@ var ( _ bridgev2.LoginProcessDisplayAndWait = (*WALogin)(nil) _ bridgev2.LoginProcessUserInput = (*WALogin)(nil) _ bridgev2.LoginProcessWithOverride = (*WALogin)(nil) + _ bridgev2.LoginProcessWebAuthn = (*WALogin)(nil) ) const LoginConnectWait = 15 * time.Second @@ -262,6 +273,17 @@ func (wl *WALogin) handleEvent(rawEvt any) { case *events.ClientOutdated: wl.Log.Error().Msg("Got client outdated error") wl.LoginError = ErrLoginClientOutdated + case *events.PairPasskeyRequest: + wl.PasskeyRequestData = evt + wl.PasskeyRequest.Set() + return + case *events.PairPasskeyConfirmation: + wl.PasskeyConfirmationData = evt + wl.PasskeyConfirmation.Set() + return + case *events.PairPasskeyError: + wl.Log.Err(evt.Error).Msg("Got passkey error") + wl.LoginError = evt.Error case *events.PairSuccess: wl.Log.Info().Any("event_data", evt).Msg("Got pair successful event") wl.LoginSuccess = evt @@ -289,10 +311,14 @@ func (wl *WALogin) handleEvent(rawEvt any) { func (wl *WALogin) Wait(ctx context.Context) (*bridgev2.LoginStep, error) { if wl.PhoneCode { - err := wl.LoginComplete.Wait(ctx) - if err != nil { + select { + case <-ctx.Done(): wl.Cancel() - return nil, err + return nil, ctx.Err() + case <-wl.PasskeyRequest.GetChan(): + return wl.makePasskeyStep() + case <-wl.LoginComplete.GetChan(): + // continue } } else { prevIndex := int(wl.PrevQRIndex.Load()) @@ -319,9 +345,16 @@ func (wl *WALogin) Wait(ctx context.Context) (*bridgev2.LoginStep, error) { case <-ctx.Done(): wl.Cancel() return nil, ctx.Err() + case <-wl.PasskeyRequest.GetChan(): + return wl.makePasskeyStep() case <-wl.LoginComplete.GetChan(): + // continue } } + return wl.onLoginComplete(ctx) +} + +func (wl *WALogin) onLoginComplete(ctx context.Context) (*bridgev2.LoginStep, error) { if wl.LoginError != nil { wl.Log.Debug().Err(wl.LoginError).Msg("Login completed with error") wl.Cancel() @@ -372,6 +405,66 @@ func (wl *WALogin) Wait(ctx context.Context) (*bridgev2.LoginStep, error) { }, nil } +func (wl *WALogin) makePasskeyStep() (*bridgev2.LoginStep, error) { + pubkeyData, err := json.Marshal(wl.PasskeyRequestData.PublicKey) + if err != nil { + wl.Log.Err(err).Msg("Failed to marshal public key data") + return nil, err + } + return &bridgev2.LoginStep{ + Type: bridgev2.LoginStepTypeWebAuthn, + StepID: LoginStepIDPasskey, + WebAuthnParams: &bridgev2.LoginWebAuthnParams{ + URL: "https://web.whatsapp.com", + PublicKey: pubkeyData, + }, + }, nil +} + +func (wl *WALogin) SubmitWebAuthnResponse(ctx context.Context, rawResp json.RawMessage) (*bridgev2.LoginStep, error) { + var resp types.WebAuthnResponse + err := json.Unmarshal(rawResp, &resp) + if err != nil { + wl.Log.Err(err).Msg("Failed to unmarshal WebAuthn response") + wl.Cancel() + return nil, err + } + err = wl.Client.SendPasskeyResponse(ctx, &resp) + if err != nil { + wl.Log.Err(err).Msg("Failed to send WebAuthn response") + wl.Cancel() + return nil, err + } + select { + case <-ctx.Done(): + wl.Cancel() + return nil, ctx.Err() + case <-wl.PasskeyConfirmation.GetChan(): + if !wl.PasskeyConfirmationData.SkipHandoffUX { + wl.Cancel() + return nil, bridgev2.RespError{ + ErrCode: "FI.MAU.WHATSAPP.NOT_IMPLEMENTED", + Err: "Displaying WebAuthn pairing confirmation codes is not yet implemented", + StatusCode: http.StatusBadRequest, + } + } + err = wl.Client.SendPasskeyConfirmation(ctx) + if err != nil { + wl.Log.Err(err).Msg("Failed to send WebAuthn confirmation") + wl.Cancel() + return nil, err + } + case <-wl.LoginComplete.GetChan(): + } + select { + case <-ctx.Done(): + wl.Cancel() + return nil, ctx.Err() + case <-wl.LoginComplete.GetChan(): + } + return wl.onLoginComplete(ctx) +} + func (wl *WALogin) Cancel() { wl.Closed.Store(true) wl.Client.RemoveEventHandler(wl.EventHandlerID) From f3ca7ac1c39401e99ef05112082b1d2bc022df83 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 2 Jul 2026 00:15:15 +0300 Subject: [PATCH 28/29] 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 c16824c..bf909be 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( golang.org/x/sync v0.21.0 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.28.2-0.20260701195654-d33ead4e1d86 + maunium.net/go/mautrix v0.28.2-0.20260701211445-bbd547b7edc7 ) require ( diff --git a/go.sum b/go.sum index d38c8cb..5be58b3 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.28.2-0.20260701195654-d33ead4e1d86 h1:OKNg/klCID3MoZHuhpMSQThvWOqKtbCrXC6HjlY4+QA= -maunium.net/go/mautrix v0.28.2-0.20260701195654-d33ead4e1d86/go.mod h1:mWXQNmOlrq4VTDU9f1HO03BSIswdUIyyY4wUKHqwzzY= +maunium.net/go/mautrix v0.28.2-0.20260701211445-bbd547b7edc7 h1:zqwDd/c02keWWjn5AqafdKnahtGChn595Vuw6af5WdU= +maunium.net/go/mautrix v0.28.2-0.20260701211445-bbd547b7edc7/go.mod h1:mWXQNmOlrq4VTDU9f1HO03BSIswdUIyyY4wUKHqwzzY= From 7753bb3999305ea7c7f1825348a56e4d73f30055 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 2 Jul 2026 15:40:33 +0300 Subject: [PATCH 29/29] 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 bf909be..9dc2e58 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( golang.org/x/sync v0.21.0 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 - maunium.net/go/mautrix v0.28.2-0.20260701211445-bbd547b7edc7 + maunium.net/go/mautrix v0.28.2-0.20260702123919-bf2dbb2aa895 ) require ( diff --git a/go.sum b/go.sum index 5be58b3..1d4b8af 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.28.2-0.20260701211445-bbd547b7edc7 h1:zqwDd/c02keWWjn5AqafdKnahtGChn595Vuw6af5WdU= -maunium.net/go/mautrix v0.28.2-0.20260701211445-bbd547b7edc7/go.mod h1:mWXQNmOlrq4VTDU9f1HO03BSIswdUIyyY4wUKHqwzzY= +maunium.net/go/mautrix v0.28.2-0.20260702123919-bf2dbb2aa895 h1:oe87/mP1vo4LnA6CJDEfyg5ZiAOar2quJNypfO8eS4Y= +maunium.net/go/mautrix v0.28.2-0.20260702123919-bf2dbb2aa895/go.mod h1:mWXQNmOlrq4VTDU9f1HO03BSIswdUIyyY4wUKHqwzzY=