Skip to content

Commit

Permalink
client: add support for connect-once background resync interface
Browse files Browse the repository at this point in the history
  • Loading branch information
tulir committed Jan 15, 2025
1 parent 989f903 commit c3a263a
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 5 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ require (
golang.org/x/sync v0.10.0
google.golang.org/protobuf v1.36.1
gopkg.in/yaml.v3 v3.0.1
maunium.net/go/mautrix v0.22.2-0.20250113200949-53a56684d3d3
maunium.net/go/mautrix v0.22.2-0.20250115130529-b17a8cd74cf6
)

require (
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -101,5 +101,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
maunium.net/go/mauflag v1.0.0 h1:YiaRc0tEI3toYtJMRIfjP+jklH45uDHtT80nUamyD4M=
maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA=
maunium.net/go/mautrix v0.22.2-0.20250113200949-53a56684d3d3 h1:ouuSo9unoAHaEze2hOo7z7lQG+rTjxhvXlXngExQwqg=
maunium.net/go/mautrix v0.22.2-0.20250113200949-53a56684d3d3/go.mod h1:07i96D7BALyuAqxFhRzvaId8FC9NABgRQBPY5HWndf4=
maunium.net/go/mautrix v0.22.2-0.20250115130529-b17a8cd74cf6 h1:NwaMR4yLqkkTkQMP4Vv8wbo1PY/8qgVQ1CrexUSd/Wc=
maunium.net/go/mautrix v0.22.2-0.20250115130529-b17a8cd74cf6/go.mod h1:07i96D7BALyuAqxFhRzvaId8FC9NABgRQBPY5HWndf4=
34 changes: 32 additions & 2 deletions pkg/connector/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,16 @@ type WhatsAppClient struct {
directMediaRetries map[networkid.MessageID]*directMediaRetry
directMediaLock sync.Mutex
mediaRetryLock *semaphore.Weighted
offlineSyncWaiter chan error

lastPhoneOfflineWarning time.Time
isNewLogin bool
}

var (
_ bridgev2.NetworkAPI = (*WhatsAppClient)(nil)
_ bridgev2.PushableNetworkAPI = (*WhatsAppClient)(nil)
_ bridgev2.NetworkAPI = (*WhatsAppClient)(nil)
_ bridgev2.PushableNetworkAPI = (*WhatsAppClient)(nil)
_ bridgev2.BackgroundSyncingNetworkAPI = (*WhatsAppClient)(nil)
)

var pushCfg = &bridgev2.PushConfig{
Expand Down Expand Up @@ -171,6 +173,34 @@ func (wa *WhatsAppClient) Connect(ctx context.Context) {
}
}

func (wa *WhatsAppClient) notifyOfflineSyncWaiter(err error) {
if wa.offlineSyncWaiter != nil {
wa.offlineSyncWaiter <- err
}
}

func (wa *WhatsAppClient) ConnectBackground(ctx context.Context) error {
if wa.Client == nil {
return bridgev2.ErrNotLoggedIn
}
wa.offlineSyncWaiter = make(chan error)
wa.Main.firstClientConnectOnce.Do(wa.Main.onFirstClientConnect)
if err := wa.Main.updateProxy(ctx, wa.Client, false); err != nil {
zerolog.Ctx(ctx).Err(err).Msg("Failed to update proxy")
}
err := wa.Client.Connect()
if err != nil {
return err
}
defer wa.Disconnect()
select {
case <-ctx.Done():
return ctx.Err()
case err = <-wa.offlineSyncWaiter:
return err
}
}

func (wa *WhatsAppClient) startLoops() {
ctx, cancel := context.WithCancel(context.Background())
oldStop := wa.stopLoops.Swap(&cancel)
Expand Down
8 changes: 8 additions & 0 deletions pkg/connector/handlewhatsapp.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,14 +181,17 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) {
} else {
log.Info().Msg("Offline sync completed")
}
wa.notifyOfflineSyncWaiter(nil)
case *events.LoggedOut:
wa.handleWALogout(evt.Reason, evt.OnConnect)
wa.notifyOfflineSyncWaiter(fmt.Errorf("logged out: %s", evt.Reason))
case *events.Disconnected:
// Don't send the normal transient disconnect state if we're already in a different transient disconnect state.
// TODO remove this if/when the phone offline state is moved to a sub-state of CONNECTED
if wa.UserLogin.BridgeState.GetPrev().Error != WAPhoneOffline && wa.PhoneRecentlySeen(false) {
wa.UserLogin.BridgeState.Send(status.BridgeState{StateEvent: status.StateTransientDisconnect, Error: WADisconnected})
}
wa.notifyOfflineSyncWaiter(fmt.Errorf("disconnected"))
case *events.StreamError:
var message string
if evt.Code != "" {
Expand All @@ -203,8 +206,10 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) {
Error: WAStreamError,
Message: message,
})
wa.notifyOfflineSyncWaiter(fmt.Errorf("stream error: %s", message))
case *events.StreamReplaced:
wa.UserLogin.BridgeState.Send(status.BridgeState{StateEvent: status.StateUnknownError, Error: WAStreamReplaced})
wa.notifyOfflineSyncWaiter(fmt.Errorf("stream replaced"))
case *events.KeepAliveTimeout:
wa.UserLogin.BridgeState.Send(status.BridgeState{StateEvent: status.StateTransientDisconnect, Error: WAKeepaliveTimeout})
case *events.KeepAliveRestored:
Expand All @@ -216,15 +221,18 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) {
Error: status.BridgeStateErrorCode(fmt.Sprintf("wa-connect-failure-%d", evt.Reason)),
Message: fmt.Sprintf("Unknown connection failure: %s (%s)", evt.Reason, evt.Message),
})
wa.notifyOfflineSyncWaiter(fmt.Errorf("connection failure: %s (%s)", evt.Reason, evt.Message))
case *events.ClientOutdated:
wa.UserLogin.Log.Error().Msg("Got a client outdated connect failure. The bridge is likely out of date, please update immediately.")
wa.UserLogin.BridgeState.Send(status.BridgeState{StateEvent: status.StateUnknownError, Error: WAClientOutdated})
wa.notifyOfflineSyncWaiter(fmt.Errorf("client outdated"))
case *events.TemporaryBan:
wa.UserLogin.BridgeState.Send(status.BridgeState{
StateEvent: status.StateBadCredentials,
Error: WATemporaryBan,
Message: evt.String(),
})
wa.notifyOfflineSyncWaiter(fmt.Errorf("temporary ban: %s", evt.String()))
default:
log.Debug().Type("event_type", rawEvt).Msg("Unhandled WhatsApp event")
}
Expand Down

0 comments on commit c3a263a

Please # to comment.