From: Anderson A. Giacomolli Date: Mon, 9 Nov 2026 18:12:48 +0000 (-0300) Subject: Keep retries after a rejected pair request X-Git-Url: http://git.giacomolli.uk/onechat/commitdiff_plain/786c2b0551517d4befaa35be2c7743f65bd5f1c4?ds=inline Keep retries after a rejected pair request Keep starting connection retries active when a pair request finds an existing link. Only the unlink request stops the retry timer, while logout keeps its stop path. Rejecting a pair request during an outage must not leave WhatsApp disconnected until a restart. --- diff --git a/README.rst b/README.rst index 0cc4b1e..1c504bf 100644 --- a/README.rst +++ b/README.rst @@ -359,14 +359,14 @@ memory tree summarizes it with model calls, which use the quota. When the session holds a linked device, the daemon connects at its start. If the first connection fails, it tries again after 2 seconds, then doubles the wait up to 5 minutes. The inbox and commands stay -active during each wait. An unlink, logout, or pair command stops these -retries. After a network failure of an open connection, the library -connects again by itself. When a disconnection lasts 10 minutes, all -clients get one notice, and one more when the connection comes back. -When WhatsApp ends the connection for good, for example because another -program uses the same session, all clients get a notice at once. When -the user removes the device in the phone, the daemon deletes the session -and tells all clients one time. +active during each wait. An unlink or a logout stops these retries. +After a network failure of an open connection, the library connects +again by itself. When a disconnection lasts 10 minutes, all clients get +one notice, and one more when the connection comes back. When WhatsApp +ends the connection for good, for example because another program uses +the same session, all clients get a notice at once. When the user +removes the device in the phone, the daemon deletes the session and +tells all clients one time. *********** Schedules diff --git a/whatsapp.go b/whatsapp.go index e02e8c6..c3db573 100644 --- a/whatsapp.go +++ b/whatsapp.go @@ -209,10 +209,10 @@ type WhatsApp struct { // failed first connection starts the same outage notice as a lost connection. A // timer in the select retries it with a doubled wait from 2 seconds to 5 // minutes, without a retry goroutine or a wait that blocks commands or the -// inbox. A connection, unlink, logout, pair command, or ctx end stops retries. -// The library owns retries after a connection opens. Run returns the error of a -// write of the log or of the state file, which stops the daemon, because then -// messages would be lost. +// inbox. A connection, unlink, logout, or ctx end stops retries. The library +// owns retries after a connection opens. Run returns the error of a write of +// the log or of the state file, which stops the daemon, because then messages +// would be lost. func (w *WhatsApp) Run(ctx context.Context, l *TurnLoop, notify func(Event)) error { w.mutex.Lock() w.notify = notify @@ -277,13 +277,13 @@ func (w *WhatsApp) Run(ctx context.Context, l *TurnLoop, notify func(Event)) err return err } case request := <-w.requests: - stopRetry() cancel() pairing.Wait() cancel = func() {} switch { case request.unlink: + stopRetry() err = w.unlink(ctx, l, request.answer) if err != nil { return err diff --git a/whatsapp_test.go b/whatsapp_test.go index c14571b..db04dc7 100644 --- a/whatsapp_test.go +++ b/whatsapp_test.go @@ -106,6 +106,35 @@ func TestWhatsAppFirstConnectionRetriesStopWhenContextEnds(t *testing.T) { }) } +func TestWhatsAppLinkedPairRequestKeepsFirstConnectionRetries(t *testing.T) { + synctest.Test(t, func(t *testing.T) { + test := whatsappRetryTest(t) + test.fake.connectErr = errors.New("network unavailable") + test.start(t) + synctest.Wait() + + answers := make(chan string, 1) + test.whatsapp.Command("pair 15550001111", func(text string) { + answers <- text + }) + require.Equal(t, "WhatsApp is linked already. Run /whatsapp unlink first.", <-answers) + synctest.Wait() + test.fake.mutex.Lock() + test.fake.connectErr = nil + test.fake.mutex.Unlock() + time.Sleep(whatsappRetryDelay) + synctest.Wait() + require.Len(t, test.fake.Connections(), 2) + require.True(t, test.fake.Emit(&events.Connected{})) + require.True(t, test.fake.Emit(&events.OfflineSyncCompleted{})) + require.Contains(t, test.whatsapp.Status(), "linked and connected since") + time.Sleep(whatsappRetryLimit) + synctest.Wait() + require.Len(t, test.fake.Connections(), 2) + require.Equal(t, "first connect failed, retrying: network unavailable\nconnected again after 2s\n", test.logs.String()) + }) +} + func TestWhatsAppFirstConnectionRetriesStopForLinkChanges(t *testing.T) { testCases := []struct { name string @@ -123,12 +152,6 @@ func TestWhatsAppFirstConnectionRetriesStopForLinkChanges(t *testing.T) { test.fake.Emit(&events.LoggedOut{}) }, }, - { - name: "pair command", - stop: func(test *WhatsAppRetryTest) { - test.whatsapp.Command("pair 15550001111", func(text string) {}) - }, - }, { name: "permanent disconnect", stop: func(test *WhatsAppRetryTest) {