diff --git a/go.mod b/go.mod index 9dc2e58..85f5c6c 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-20260630180629-b572e5bcb92b + go.mau.fi/whatsmeow v0.0.0-20260616120636-eaa388b4e537 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.2-0.20260702123919-bf2dbb2aa895 + maunium.net/go/mautrix v0.28.1 ) require ( diff --git a/go.sum b/go.sum index 1d4b8af..e0a405c 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-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/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= @@ -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.20260702123919-bf2dbb2aa895 h1:oe87/mP1vo4LnA6CJDEfyg5ZiAOar2quJNypfO8eS4Y= -maunium.net/go/mautrix v0.28.2-0.20260702123919-bf2dbb2aa895/go.mod h1:mWXQNmOlrq4VTDU9f1HO03BSIswdUIyyY4wUKHqwzzY= +maunium.net/go/mautrix v0.28.1 h1:Hic3oDMPbLbQu1fhboTRAKZcORMjzzkjxsa+SGk60b0= +maunium.net/go/mautrix v0.28.1/go.mod h1:mWXQNmOlrq4VTDU9f1HO03BSIswdUIyyY4wUKHqwzzY= diff --git a/pkg/connector/backfill.go b/pkg/connector/backfill.go index ee92ac2..0e6de76 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) || errors.Is(err, whatsmeow.ErrGroupNotFound) { - log.Debug().Err(err).Stringer("chat_jid", conv.ChatJID). + if errors.Is(err, whatsmeow.ErrNotInGroup) { + log.Debug().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 { diff --git a/pkg/connector/handlewhatsapp.go b/pkg/connector/handlewhatsapp.go index 2b9b145..30a9cd8 100644 --- a/pkg/connector/handlewhatsapp.go +++ b/pkg/connector/handlewhatsapp.go @@ -126,20 +126,6 @@ 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/login.go b/pkg/connector/login.go index 5da419f..b70ab1a 100644 --- a/pkg/connector/login.go +++ b/pkg/connector/login.go @@ -2,7 +2,6 @@ package connector import ( "context" - "encoding/json" "errors" "fmt" "net/http" @@ -13,7 +12,6 @@ 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" @@ -27,7 +25,6 @@ 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" @@ -94,11 +91,9 @@ func (wa *WhatsAppConnector) CreateLogin(_ context.Context, user *bridgev2.User, Bool("phone_code", flowID == LoginFlowIDPhone). Logger(), - WaitForQRs: exsync.NewEvent(), - LoginComplete: exsync.NewEvent(), - PasskeyRequest: exsync.NewEvent(), - PasskeyConfirmation: exsync.NewEvent(), - Received515: exsync.NewEvent(), + WaitForQRs: exsync.NewEvent(), + LoginComplete: exsync.NewEvent(), + Received515: exsync.NewEvent(), }, nil } @@ -119,11 +114,6 @@ 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 } @@ -132,7 +122,6 @@ var ( _ bridgev2.LoginProcessDisplayAndWait = (*WALogin)(nil) _ bridgev2.LoginProcessUserInput = (*WALogin)(nil) _ bridgev2.LoginProcessWithOverride = (*WALogin)(nil) - _ bridgev2.LoginProcessWebAuthn = (*WALogin)(nil) ) const LoginConnectWait = 15 * time.Second @@ -273,17 +262,6 @@ 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 @@ -311,14 +289,10 @@ func (wl *WALogin) handleEvent(rawEvt any) { func (wl *WALogin) Wait(ctx context.Context) (*bridgev2.LoginStep, error) { if wl.PhoneCode { - select { - case <-ctx.Done(): + err := wl.LoginComplete.Wait(ctx) + if err != nil { wl.Cancel() - return nil, ctx.Err() - case <-wl.PasskeyRequest.GetChan(): - return wl.makePasskeyStep() - case <-wl.LoginComplete.GetChan(): - // continue + return nil, err } } else { prevIndex := int(wl.PrevQRIndex.Load()) @@ -345,16 +319,9 @@ 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() @@ -405,66 +372,6 @@ func (wl *WALogin) onLoginComplete(ctx context.Context) (*bridgev2.LoginStep, er }, 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) diff --git a/pkg/connector/phoneping.go b/pkg/connector/phoneping.go index 71094c9..ce38079 100644 --- a/pkg/connector/phoneping.go +++ b/pkg/connector/phoneping.go @@ -37,13 +37,6 @@ 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/connector/userinfo.go b/pkg/connector/userinfo.go index e827955..2b94047 100644 --- a/pkg/connector/userinfo.go +++ b/pkg/connector/userinfo.go @@ -29,8 +29,7 @@ var ResyncLoopInterval = 4 * time.Hour var ResyncJitterSeconds = 3600 func (wa *WhatsAppClient) EnqueueGhostResync(ghost *bridgev2.Ghost) { - lastSync := ghost.Metadata.(*waid.GhostMetadata).LastSync.Time - if lastSync.Add(ResyncMinInterval).After(time.Now()) { + if ghost.Metadata.(*waid.GhostMetadata).LastSync.Add(ResyncMinInterval).After(time.Now()) { return } wa.resyncQueueLock.Lock() @@ -44,7 +43,6 @@ 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() @@ -52,8 +50,7 @@ func (wa *WhatsAppClient) EnqueueGhostResync(ghost *bridgev2.Ghost) { func (wa *WhatsAppClient) EnqueuePortalResync(portal *bridgev2.Portal, allowDM bool) { jid, _ := waid.ParsePortalID(portal.ID) - lastSync := portal.Metadata.(*waid.PortalMetadata).LastSync.Time - if lastSync.Add(ResyncMinInterval).After(time.Now()) { + if portal.Metadata.(*waid.PortalMetadata).LastSync.Add(ResyncMinInterval).After(time.Now()) { return } else if !allowDM && jid.Server != types.GroupServer { return @@ -64,7 +61,6 @@ 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() diff --git a/pkg/msgconv/wa-media.go b/pkg/msgconv/wa-media.go index 9964800..34c529b 100644 --- a/pkg/msgconv/wa-media.go +++ b/pkg/msgconv/wa-media.go @@ -27,7 +27,6 @@ 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" @@ -130,10 +129,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, exfmt.Pluralizable("image")(int(msg.GetExpectedImageCount()))) + parts = append(parts, fmt.Sprintf("%d images", msg.GetExpectedImageCount())) } if msg.GetExpectedVideoCount() > 0 { - parts = append(parts, exfmt.Pluralizable("video")(int(msg.GetExpectedVideoCount()))) + parts = append(parts, fmt.Sprintf("%d videos", msg.GetExpectedVideoCount())) } var partDesc string if len(parts) > 0 { diff --git a/pkg/waid/dbmeta.go b/pkg/waid/dbmeta.go index 1e54426..59e9a9e 100644 --- a/pkg/waid/dbmeta.go +++ b/pkg/waid/dbmeta.go @@ -41,8 +41,7 @@ type UserLoginMetadata struct { AppStateRecoveryAttempted map[appstate.WAPatchName]time.Time `json:"app_state_recovery_attempted,omitempty"` - HistorySyncPortalsNeedCreating bool `json:"history_sync_portals_need_creating,omitempty"` - ReachoutTimelockUntil time.Time `json:"reachout_timelock_until,omitempty"` + HistorySyncPortalsNeedCreating bool `json:"history_sync_portals_need_creating,omitempty"` MData json.RawMessage `json:"mdata,omitempty"` }