home · contact · privacy
Have /disconnect abort auto-connect.
authorChristian Heller <c.heller@plomlompom.de>
Mon, 3 Nov 2025 06:00:20 +0000 (07:00 +0100)
committerChristian Heller <c.heller@plomlompom.de>
Mon, 3 Nov 2025 06:00:20 +0000 (07:00 +0100)
src/ircplom/client.py
src/ircplom/client_tui.py
src/tests/_timeout_retries.test
src/tests/connect_disconnect_legalities.test

index e16c516ea209a6064d8a8cf397e4f7165f881651..9b66efd5467761071fa08a0243812c6895ecf4d2 100644 (file)
@@ -824,11 +824,13 @@ class Client(ABC, ClientQueueMixin):
     conn: Optional[IrcConnection] = None
     _cls_conn: type[IrcConnection] = IrcConnection
     _expected_pong: str
-    _retry_connect_in_s: int = -1
+    _retry_connect_in_s: int
     _retry_connect_id: Optional[UUID] = None
+    _RETRY_CONNECT_IN_S_OFF = 0
 
     def __init__(self, conn_setup: IrcConnSetup, channels: set[str], **kwargs
                  ) -> None:
+        self._retry_connect_in_s = self._RETRY_CONNECT_IN_S_OFF
         self.client_id = conn_setup.hostname
         self._autojoins = channels
         super().__init__(client_id=self.client_id, **kwargs)
@@ -864,24 +866,27 @@ class Client(ABC, ClientQueueMixin):
         # Do this in a thread, not to block flow of other (e.g. TUI) events.
         Thread(target=connect, daemon=True, args=(self,)).start()
 
+    @property
+    def _retry_connect_on(self) -> bool:
+        return self._retry_connect_in_s > self._RETRY_CONNECT_IN_S_OFF
+
     def _on_connecting_exception(self, e: IrcConnException) -> None:
         self.db.connection_state = f'failed to connect: {e}'
         if e.retry:
-            if not self._retry_connect_in_s > 0:
+            if not self._retry_connect_on:
                 self._retry_connect_in_s = 1
             self._delayed_retry_connect()
 
     def _on_connected(self) -> None:
         self._expected_pong = ''
-        self._retry_connect_in_s = -1
+        self._retry_connect_in_s = self._RETRY_CONNECT_IN_S_OFF
         self.db.connection_state = 'connected'
         self.caps.start_negotation()
         self.send('USER', self.db.user_wanted, '0', '*', self.db.realname)
         self.send('NICK', self.db.nick_wanted,)
 
     def _delayed_retry_connect(self) -> None:
-        def delayed_connect(self, wait_s: int, retry_connect_id: UUID
-                            ) -> None:
+        def delayed_connect(self, wait_s: int, retry_connect_id: UUID) -> None:
             sleep(wait_s)
             if self._retry_connect_id == retry_connect_id:
                 self._client_trigger('connect')
@@ -896,7 +901,7 @@ class Client(ABC, ClientQueueMixin):
 
     def consider_retry(self) -> None:
         'Interpret .db.connection_state, on triggers set retry connect.'
-        if self._retry_connect_in_s > 0:
+        if self._retry_connect_on:
             return
         for _ in [re for re in _DISCONNECT_MSG_REGEXES_TO_RETRY_ON
                   if re_search(re, self.db.connection_state, re_IGNORECASE)]:
@@ -911,7 +916,7 @@ class Client(ABC, ClientQueueMixin):
         if self.conn:
             self.conn.close()
         self.conn = None
-        if self._retry_connect_in_s > 0:
+        if self._retry_connect_on:
             self._delayed_retry_connect()
 
     def on_handled_loop_exception(self, e: IrcConnException) -> None:
index eb02f87b749adc7a0412c1895bc9f740013f2374..8b0b1197c0d4b1109004d2f73125de924cb94a68 100644 (file)
@@ -87,8 +87,8 @@ class _ClientWindow(Window, ClientQueueMixin):
         self._client_trigger('send_w_params_tuple', verb=verb, params=params)
 
     def cmd__disconnect(self, quit_msg: str = 'ircplom says bye') -> None:
-        'Send QUIT command to server.'
-        self._send_msg('QUIT', (quit_msg,))
+        'Ask to disconnect, or end auto-connect attempts if disconnected.'
+        self._client_trigger('disconnect', quit_msg=quit_msg)
 
     def cmd__reconnect(self) -> None:
         'Attempt reconnection.'
@@ -509,6 +509,19 @@ class ClientKnowingTui(Client):
         elif self.send('PRIVMSG', chat_target, msg):
             self.db.messaging('').to(chat_target).privmsg = msg  # type: ignore
 
+    def disconnect(self, quit_msg: str) -> None:
+        'Send QUIT if connected, else end auto-connect attempts.'
+        if self.conn:
+            self.send('QUIT', quit_msg)
+            return
+        prefix = 'already disconnected'
+        if self._retry_connect_on:
+            self._retry_connect_id = None
+            self._tui_trigger('log', prefix_char=_LOG_PREFIX_SERVER,
+                              msg=f'{prefix}, stopped connecting attempts')
+        else:
+            self._tui_alert_trigger(f'{prefix} and not attempting connect')
+
     def reconnect(self) -> None:
         'Catch /reconnect, only initiate if not connected, else complain back.'
         if self.conn:
index 5148319c0e6b906ef8138f208523bb7356281895..dbe3a35d4be24a6f08f544cb1eca816ae0afa6b7 100644 (file)
@@ -55,7 +55,7 @@ insert conn-init-retries : +1
 # with TestingClient finally having reduced port to 10000, connecting works now
 insert conn : +1
 
-# test retry chain also started by in-connection timeout
+# check retry chain also started by in-connection timeout
 servermsg 0 timeout
 log 1 > PING :what's up?
 servermsg 0 timeout
@@ -65,7 +65,12 @@ insert isupport-clear : +1
 log 1 $ connection_state set to: []
 log , $ DISCONNECTED
 log 1 $ will retry connecting in 1 seconds
-insert conn : +1
+
+# check /disconnect aborts retry chain
+> /window 1
+> /disconnect
+log 1 $ already disconnected, stopped connecting attempts
+wait 1
 
 # on second server, check timed auto-retries don't activate after manual intervention
 > /connect baz.bar.foo:10003 foo:bar baz:foobarbazquux
index 4ba5a6ecce319766a76a46597413782e6f894c9a..91a40610a1b6ffcef9184bbdb2e177ea8cef9972 100644 (file)
@@ -10,6 +10,10 @@ log , $ CONNECTED
 log 1 > CAP LS :302
 log 1 > USER baz 0 * :bar
 log 1 > NICK :foo
+× disconnecting
+insert isupport-clear : +1
+log 1 $ connection_state set to: []
+log , $ DISCONNECTED
 ×
 
 # to prepare, initiate connection
@@ -37,14 +41,20 @@ log 1 $ not re-connecting since already connected
 log 1 > QUIT :ircplom says bye
 loggedservermsg 0 1 < ERROR :Closing link: (whatever@whatever.com) [Quit: ircplom says bye]
 log 1 $ connection_state set to: [Closing link: (whatever@whatever.com) [Quit: ircplom says bye]]
-insert isupport-clear : +1
-log 1 $ connection_state set to: []
-log , $ DISCONNECTED
+insert disconnecting : +1
 
 # fail to disconnect when already disconnected
 > /disconnect
-log 1 $ cannot send, connection seems closed
+log 1 $ already disconnected and not attempting connect
 
 # succeed to re-connect after disconnect
 > /reconnect
 insert connecting : +1
+
+# allow /disconnect outside connection for auto-connect stops
+loggedservermsg 0 1 < ERROR :Closing link: (Connection timed out)
+log 1 $ connection_state set to: [Closing link: (Connection timed out)]
+insert disconnecting : +1
+log 1 $ will retry connecting in 1 seconds
+> /disconnect
+log 1 $ already disconnected, stopped connecting attempts