From a456b502dbf16ec3fc99c2574bde892bc1ae9306 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Tue, 27 Oct 2026 20:06:55 -0400 Subject: [PATCH] discord_radio: retained desired redelivery no longer undoes a local pause (node-26#34) A local pause records the applied seq; only a strictly newer seq from C2 lifts it. The retained message the broker redelivers on reconnect carries the same seq, so it now leaves the bots logged out. Co-Authored-By: Claude Opus 5.5 --- drb-edge-node/app/internal/discord_radio.py | 18 ++++++++++++++++-- drb-edge-node/tests/test_discord_radio.py | 12 ++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/drb-edge-node/app/internal/discord_radio.py b/drb-edge-node/app/internal/discord_radio.py index c005ab5..d9cb3eb 100644 --- a/drb-edge-node/app/internal/discord_radio.py +++ b/drb-edge-node/app/internal/discord_radio.py @@ -183,6 +183,10 @@ class RadioManager: self._mqtt_down_since: Optional[float] = None self._failsafe = False self._paused = False + # The applied seq at the moment of a local pause (node-26#34). The + # broker redelivers the retained desired message on every reconnect + # with the SAME seq; only a strictly newer seq from C2 lifts the pause. + self._paused_at_seq: Optional[int] = None self._ticks = 0 # ------------------------------------------------------------------ @@ -210,15 +214,25 @@ class RadioManager: logger.error(f"Malformed Discord desired stream entry ignored: {s!r}") self._ensure_watchdog() self._desired = wanted - self._paused = False + if self._paused and self._redelivery_of_paused_state(seq): + logger.info(f"Discord desired seq {seq} is a redelivery — staying paused locally.") + else: + self._paused = False + self._paused_at_seq = None await self.reconcile() if seq is not None: self._applied_seq = seq await self.publish_actual() + def _redelivery_of_paused_state(self, seq: Optional[int]) -> bool: + """True when `seq` is not newer than what was applied at pause time.""" + return seq is not None and self._paused_at_seq is not None and seq <= self._paused_at_seq + async def pause_locally(self) -> None: - """Local-dashboard stop: log every bot out until C2 sends a new list.""" + """Local-dashboard stop: log every bot out until C2 sends a NEW list + (a seq strictly greater than the one applied now — node-26#34).""" self._paused = True + self._paused_at_seq = self._applied_seq await self.reconcile() await self.publish_actual() diff --git a/drb-edge-node/tests/test_discord_radio.py b/drb-edge-node/tests/test_discord_radio.py index 30e2856..5c3c2e4 100644 --- a/drb-edge-node/tests/test_discord_radio.py +++ b/drb-edge-node/tests/test_discord_radio.py @@ -263,6 +263,18 @@ async def test_local_pause_logs_out_until_next_desired(env): assert env.mgr.is_connected +async def test_retained_redelivery_does_not_undo_local_pause(env): + """node-26#34: the broker redelivers the retained desired message (same + seq) on every reconnect; that must not log the bots back in.""" + await env.mgr.apply_desired(_desired(4, (1, 11, "t1"))) + await env.mgr.pause_locally() + await env.mgr.apply_desired(_desired(4, (1, 11, "t1"))) + assert not env.mgr.is_connected + assert env.published[-1]["streams"]["1:11"]["state"] == "paused_locally" + await env.mgr.apply_desired(_desired(5, (1, 11, "t1"))) + assert env.mgr.is_connected + + async def test_no_message_content_intent_on_real_client(): client = discord_radio._default_client_factory() try: