Watch
1
0
Fork
You've already forked mautrix-whatsapp
0
mirror of https://github.com/mautrix/whatsapp.git synced 2026-08-22 12:04:55 -04:00

Compare commits

...
Author SHA1 Message Date
Tulir Asokan
d70eb6b135 dependencies: update whatsmeow 2026-08-21 17:25:12 +03:00
Tulir Asokan
bbc9a37b30 dependencies: bump minimum Go version to 1.26 2026-08-20 18:37:07 +03:00
Tulir Asokan
ba58807b0f
backfill: clamp message timestamps in history syncs (#946) 2026-08-20 16:09:02 +03:00
Tulir Asokan
5ef4c47b44 chatinfo: always allow poll responses in power levels 2026-08-19 18:53:20 +03:00
Nick Mills-Barrett
0f1832595f
login: split connect and phone pair timeouts, increase to 30s (#947) 2026-08-19 15:41:15 +01:00
6 changed files with 60 additions and 51 deletions

View file

@ -11,14 +11,14 @@ jobs:
strategy:
fail-fast: false
matrix:
go-version: ["1.25", "1.26"]
name: Lint ${{ matrix.go-version == '1.26' && '(latest)' || '(old)' }}
go-version: ["1.26", "1.27"]
name: Lint ${{ matrix.go-version == '1.27' && '(latest)' || '(old)' }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Set up Go
uses: actions/setup-go@v6
uses: actions/setup-go@v7
with:
go-version: ${{ matrix.go-version }}
cache: true

10
go.mod
View file

@ -1,8 +1,8 @@
module go.mau.fi/mautrix-whatsapp
go 1.25.0
go 1.26.0
toolchain go1.26.6
toolchain go1.27.0
tool go.mau.fi/util/cmd/maubuild
@ -10,15 +10,15 @@ 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.10.0
go.mau.fi/util v0.10.1-0.20260820140024-eb612d936fde
go.mau.fi/webp v0.3.0
go.mau.fi/whatsmeow v0.0.0-20260816113502-fb386f152837
go.mau.fi/whatsmeow v0.0.0-20260821141805-33cfac511629
golang.org/x/image v0.45.0
golang.org/x/net v0.58.0
golang.org/x/sync v0.22.0
google.golang.org/protobuf v1.36.12
gopkg.in/yaml.v3 v3.0.1
maunium.net/go/mautrix v0.30.0
maunium.net/go/mautrix v0.30.1-0.20260820142923-ae00f2c62e1e
)
require (

12
go.sum
View file

@ -71,12 +71,12 @@ github.com/yuin/goldmark v1.8.5 h1:r6N5afV5qj/5S4UTch8agZHJ8UxNCMwX7WjkkJam2NA=
github.com/yuin/goldmark v1.8.5/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.10.0 h1:vH9IXZmfBKa96p47HxrVqEPkrj02zDJg3o4EF172+Lk=
go.mau.fi/util v0.10.0/go.mod h1:uZwpm9sK4wO2Qqy+t6QoVq29szMsRxWXp9/BkQLG4xk=
go.mau.fi/util v0.10.1-0.20260820140024-eb612d936fde h1:eMHY9dMDkNuDMWhfTbMZHbbsxj7G6mfujjKei1HaFQM=
go.mau.fi/util v0.10.1-0.20260820140024-eb612d936fde/go.mod h1:z0ZZNt4hq3FZbUKnunexE/QscCx7VkLvQSvtggc/aE8=
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-20260816113502-fb386f152837 h1:xJ13dqFcK/oPNImltlk7sumI/uiQ9EON/6g/iwpT1Nc=
go.mau.fi/whatsmeow v0.0.0-20260816113502-fb386f152837/go.mod h1:UFP0D7aj+biuFejsllcoNiHTQorh0ODFUyG4Txpqfks=
go.mau.fi/whatsmeow v0.0.0-20260821141805-33cfac511629 h1:99p9fTS1G73a1aifCVaV5MX4hDq0lVUKWGoUNoXyE3A=
go.mau.fi/whatsmeow v0.0.0-20260821141805-33cfac511629/go.mod h1:aMd13H2xFFGH9cskcvxo4Aae+TmyFN38yw+HvsrpwVg=
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.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M=
@ -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.30.0 h1:bad+q7w5tLqiHpr+oUxVI+8m8ePbV3AvoFKg2jQzPyo=
maunium.net/go/mautrix v0.30.0/go.mod h1:bb0gjxbTFOqTaAYKGw5E7j9XROUR2Sl1Etm3IbmYXbo=
maunium.net/go/mautrix v0.30.1-0.20260820142923-ae00f2c62e1e h1:ATvERyBIUXJrnL7ezP2xCLLGCNmGClmfzoQq4N3jBcA=
maunium.net/go/mautrix v0.30.1-0.20260820142923-ae00f2c62e1e/go.mod h1:Y02sBiAvfEVqK24bwVGCprmLATRZ7prWel3ZpB413e0=

View file

@ -247,12 +247,14 @@ func (wa *WhatsAppClient) handleWAHistorySync(
return c.Stringer("chat_jid", jid)
})
var minTime, maxTime, firstItemTime, lastItemTime time.Time
var minTimeIndex, maxTimeIndex int
var firstItemTime, lastItemTime time.Time
ignoredTypes := 0
messages := make([]*wadb.HistorySyncMessageTuple, 0, len(conv.GetMessages()))
for i, rawMsg := range conv.GetMessages() {
rawMessages := conv.GetMessages()
messages := make([]*wadb.HistorySyncMessageTuple, 0, len(rawMessages))
allowClamp := conv.GetCommentsCount() == 0
var newerTS uint64
for i, rawMsg := range rawMessages {
// Don't store messages that will just be skipped.
msgEvt, err := wa.Client.ParseWebMessage(jid, rawMsg.GetMessage())
if err != nil {
@ -270,20 +272,30 @@ func (wa *WhatsAppClient) handleWAHistorySync(
firstItemTime = msgEvt.Info.Timestamp
}
lastItemTime = msgEvt.Info.Timestamp
if minTime.IsZero() || msgEvt.Info.Timestamp.Before(minTime) {
minTime = msgEvt.Info.Timestamp
minTimeIndex = i
}
if maxTime.IsZero() || msgEvt.Info.Timestamp.After(maxTime) {
maxTime = msgEvt.Info.Timestamp
maxTimeIndex = i
}
msgType := getMessageType(msgEvt.Message)
if msgType == "ignore" || strings.HasPrefix(msgType, "unknown_protocol_") {
ignoredTypes++
continue
}
// Comments (replies) in announcement groups are not ordered by timestamp, so don't clamp them.
if rawMsg.GetMessage().GetCommentMetadata().GetCommentParentKey() != nil {
allowClamp = false
}
// WhatsApp has bugs where some random messages will have timestamps decades in the future.
// To ensure they don't mess up our ordering, require timestamps of older messages to be
// before the previous (newer) message.
if currentTS := rawMsg.GetMessage().GetMessageTimestamp(); newerTS > 0 && allowClamp && currentTS > newerTS {
log.Warn().
Time("current_ts", time.Unix(int64(currentTS), 0)).
Time("prev_ts", time.Unix(int64(newerTS), 0)).
Int("msg_index", i).
Str("msg_id", rawMsg.GetMessage().GetKey().GetID()).
Msg("Clamping message timestamp")
rawMsg.Message.MessageTimestamp = ptr.Ptr(newerTS)
msgEvt.Info.Timestamp = time.Unix(int64(newerTS), 0)
}
newerTS = rawMsg.GetMessage().GetMessageTimestamp()
marshaled, err := proto.Marshal(rawMsg)
if err != nil {
log.Warn().Err(err).
@ -298,13 +310,8 @@ func (wa *WhatsAppClient) handleWAHistorySync(
log.Debug().
Int("wrapped_count", len(messages)).
Int("ignored_msg_type_count", ignoredTypes).
Time("lowest_time", minTime).
Int("lowest_time_index", minTimeIndex).
Time("highest_time", maxTime).
Int("highest_time_index", maxTimeIndex).
Time("first_item_time", firstItemTime).
Time("last_item_time", lastItemTime).
Bool("highest_time_mismatch", firstItemTime != maxTime).
Dict("metadata", zerolog.Dict().
Uint32("ephemeral_expiration", conv.GetEphemeralExpiration()).
Int64("ephemeral_setting_timestamp", conv.GetEphemeralSettingTimestamp()).
@ -320,7 +327,7 @@ func (wa *WhatsAppClient) handleWAHistorySync(
Msg("Collected messages to save from history sync conversation")
if len(messages) > 0 {
err = wa.Main.DB.Conversation.Put(ctx, wadb.NewConversation(wa.UserLogin.ID, jid, conv, maxTime))
err = wa.Main.DB.Conversation.Put(ctx, wadb.NewConversation(wa.UserLogin.ID, jid, conv, firstItemTime))
if err != nil {
if stopOnError {
return fmt.Errorf("failed to save conversation metadata for %s: %w", jid, err)

View file

@ -284,14 +284,13 @@ func (wa *WhatsAppClient) wrapGroupInfo(ctx context.Context, info *types.GroupIn
Ban: ptr.Ptr(nobodyPL),
// TODO allow invites if bridge config says to allow them, or maybe if relay mode is enabled?
Events: map[event.Type]int{
event.StateRoomName: metaChangePL,
event.StateRoomAvatar: metaChangePL,
event.StateTopic: metaChangePL,
event.EventReaction: defaultPL,
event.EventRedaction: defaultPL,
event.StateRoomName: metaChangePL,
event.StateRoomAvatar: metaChangePL,
event.StateTopic: metaChangePL,
event.EventReaction: defaultPL,
event.EventRedaction: defaultPL,
event.EventUnstablePollResponse: defaultPL,
event.StateBeeperDisappearingTimer: metaChangePL,
// TODO always allow poll responses
},
},
},
@ -551,12 +550,12 @@ func (wa *WhatsAppClient) wrapNewsletterInfo(ctx context.Context, info *types.Ne
StateDefault: ptr.Ptr(nobodyPL),
Ban: ptr.Ptr(nobodyPL),
Events: map[event.Type]int{
event.StateRoomName: adminPL,
event.StateRoomAvatar: adminPL,
event.StateTopic: adminPL,
event.EventReaction: defaultPL,
event.EventRedaction: defaultPL,
// TODO always allow poll responses
event.StateRoomName: adminPL,
event.StateRoomAvatar: adminPL,
event.StateTopic: adminPL,
event.EventReaction: defaultPL,
event.EventRedaction: defaultPL,
event.EventUnstablePollResponse: defaultPL,
},
},
},

View file

@ -135,7 +135,8 @@ var (
_ bridgev2.LoginProcessWebAuthn = (*WALogin)(nil)
)
const LoginConnectWait = 15 * time.Second
const LoginConnectWait = 30 * time.Second
const LoginPairPhoneWait = 30 * time.Second
func (wl *WALogin) Start(ctx context.Context) (*bridgev2.LoginStep, error) {
wl.Main.firstClientConnectOnce.Do(wl.Main.onFirstClientConnect)
@ -193,19 +194,21 @@ func (wl *WALogin) StartWithOverride(ctx context.Context, old *bridgev2.UserLogi
}
func (wl *WALogin) SubmitUserInput(ctx context.Context, input map[string]string) (*bridgev2.LoginStep, error) {
ctx, cancel := context.WithTimeout(ctx, LoginConnectWait)
defer cancel()
err := wl.Client.Connect()
if err != nil {
wl.Log.Err(err).Msg("Failed to connect to WhatsApp for phone code login")
return nil, err
}
err = wl.WaitForQRs.Wait(ctx)
connectCtx, cancelConnect := context.WithTimeout(ctx, LoginConnectWait)
err = wl.WaitForQRs.Wait(connectCtx)
cancelConnect()
if err != nil {
wl.Log.Warn().Err(err).Msg("Timed out waiting for connection")
return nil, fmt.Errorf("failed to wait for connection: %w", err)
}
pairingCode, err := wl.Client.PairPhone(ctx, input["phone_number"], true, whatsmeow.PairClientChrome, "Chrome (Linux)")
pairCtx, cancelPair := context.WithTimeout(ctx, LoginPairPhoneWait)
defer cancelPair()
pairingCode, err := wl.Client.PairPhone(pairCtx, input["phone_number"], true, whatsmeow.PairClientChrome, "Chrome (Linux)")
if err != nil {
wl.Log.Err(err).Msg("Failed to request phone code login")
if errors.Is(err, whatsmeow.ErrPhoneNumberTooShort) {