HotFixRoomServer

This commit is contained in:
pe1hvh
2026-03-12 16:23:56 +01:00
parent 97edf22efb
commit dbecf7ac24
4 changed files with 122 additions and 40 deletions
+60 -1
View File
@@ -35,6 +35,7 @@ class CommandHandler:
self._mc = mc
self._shared = shared
self._cache = cache
self._room_sync_tasks: Dict[str, asyncio.Task] = {}
# Handler registry — add new commands here (OCP)
self._handlers: Dict[str, object] = {
@@ -406,7 +407,6 @@ class CommandHandler:
pubkey, 'ok',
f"admin={is_admin}",
)
self._shared.load_room_history(pubkey)
self._shared.set_status(
f"✅ Room login OK: {room_name}"
f"history arriving over RF…"
@@ -426,6 +426,8 @@ class CommandHandler:
except Exception as exc:
debug_print(f"login_room: defensive get_msg() error: {exc}")
self._start_room_sync(pubkey, room_name)
else:
self._shared.set_room_login_state(
pubkey, 'fail',
@@ -552,6 +554,63 @@ class CommandHandler:
)
debug_print(f"send_room_msg exception: {exc}")
def _cancel_room_sync(self, pubkey: str) -> None:
"""Cancel an active background room-history sync task."""
task = self._room_sync_tasks.pop(pubkey, None)
if task and not task.done():
task.cancel()
def _start_room_sync(self, pubkey: str, room_name: str) -> None:
"""Start a bounded background fetch loop for room history."""
self._cancel_room_sync(pubkey)
self._room_sync_tasks[pubkey] = asyncio.create_task(
self._sync_room_history(pubkey, room_name)
)
async def _sync_room_history(self, pubkey: str, room_name: str) -> None:
"""Fetch queued room messages for a short period after login.
On some serial/USB setups the SDK's auto-message fetching is
not sufficient to drain the room backlog promptly after
``LOGIN_SUCCESS``. This bounded loop polls ``get_msg()`` for a
short window so historical room messages from other users are
actually pulled into the app.
"""
idle_errors = 0
try:
for attempt in range(24):
try:
result = await self._mc.commands.get_msg()
result_type = getattr(result, 'type', None)
if result_type == EventType.ERROR:
idle_errors += 1
debug_print(
f"room_sync: get_msg ERROR for {room_name} "
f"(attempt {attempt + 1}/24, idle={idle_errors})"
)
else:
idle_errors = 0
debug_print(
f"room_sync: get_msg fetched data for {room_name} "
f"(attempt {attempt + 1}/24)"
)
except Exception as exc:
idle_errors += 1
debug_print(
f"room_sync: get_msg exception for {room_name}: {exc}"
)
if idle_errors >= 4:
break
await asyncio.sleep(2.0)
except asyncio.CancelledError:
debug_print(f"room_sync: cancelled for {room_name}")
raise
finally:
self._shared.load_room_history(pubkey)
self._room_sync_tasks.pop(pubkey, None)
# ------------------------------------------------------------------
# Callback for refresh (set by SerialWorker after construction)
# ------------------------------------------------------------------
+21 -12
View File
@@ -320,17 +320,26 @@ class EventHandler:
# --- Room Server message (txt_type 2) ---
if txt_type == 2:
# Prefer the embedded author signature when available.
# Some room-history / server-side messages arrive without a
# signature; those still belong to the room and must not fall
# through to the regular DM path.
room_pubkey = (
payload.get('room_pubkey')
or payload.get('receiver_pubkey')
or payload.get('recipient_pubkey')
or payload.get('pubkey')
or pubkey
)
author_prefix = (
signature
or payload.get('sender_pubkey_prefix', '')
or payload.get('sender_pubkey', '')
or payload.get('sender_prefix', '')
)
author = ''
if signature:
author = self._shared.get_contact_name_by_prefix(signature)
if not author:
author = signature[:8]
if author_prefix:
author = self._shared.get_contact_name_by_prefix(author_prefix)
if not author:
author = pubkey[:8] if pubkey else '?'
author = payload.get('sender_name', '') or payload.get('name', '')
if not author:
author = author_prefix[:8] if author_prefix else room_pubkey[:8] if room_pubkey else '?'
self._shared.add_message(Message.incoming(
author,
@@ -338,14 +347,14 @@ class EventHandler:
None,
snr=self._extract_snr(payload),
path_len=path_len,
sender_pubkey=pubkey,
sender_pubkey=room_pubkey,
path_hashes=path_hashes,
path_names=path_names,
message_hash=msg_hash,
))
debug_print(
f"Room msg from {author} (sig={signature or '-'}) "
f"via room {pubkey[:12]}: "
f"Room msg from {author} (sig={signature}) "
f"via room {room_pubkey[:12]}: "
f"{payload.get('text', '')[:30]}"
)
return
+1 -10
View File
@@ -258,21 +258,12 @@ class _BaseWorker(abc.ABC):
# ── LOGIN_SUCCESS handler (Room Server) ───────────────────────
def _on_login_success(self, event) -> None:
"""Synchronise Room Server login success into SharedData.
This callback is intentionally independent from the command-side
``wait_for_event(LOGIN_SUCCESS)`` path. If the library delivers the
event to subscribers before or instead of the waiter, the UI must
still transition to the logged-in state and refresh room history.
"""
payload = event.payload or {}
pubkey = payload.get("pubkey_prefix", "")
is_admin = payload.get("is_admin", False)
detail = f"admin={is_admin}"
debug_print(f"LOGIN_SUCCESS received: pubkey={pubkey}, admin={is_admin}")
self.shared.set_room_login_state(pubkey, 'ok', detail)
if pubkey:
self.shared.set_room_login_state(pubkey, 'ok', f'admin={is_admin}')
self.shared.load_room_history(pubkey)
self.shared.set_status("✅ Room login OK — messages arriving over RF…")