diff --git a/repeater/companion/frame_server.py b/repeater/companion/frame_server.py index 499bbcf..63cea6e 100644 --- a/repeater/companion/frame_server.py +++ b/repeater/companion/frame_server.py @@ -10,8 +10,16 @@ from __future__ import annotations import asyncio import logging +import struct from typing import Optional +from pymc_core.companion.constants import ( + RESP_CODE_CHANNEL_MSG_RECV, + RESP_CODE_CHANNEL_MSG_RECV_V3, + RESP_CODE_CONTACT_MSG_RECV, + RESP_CODE_CONTACT_MSG_RECV_V3, + RESP_CODE_NO_MORE_MESSAGES, +) from pymc_core.companion.frame_server import CompanionFrameServer as _BaseFrameServer from pymc_core.companion.models import QueuedMessage @@ -83,6 +91,71 @@ class CompanionFrameServer(_BaseFrameServer): path_len=msg_dict.get("path_len", 0), ) + # ----------------------------------------------------------------- + # Non-blocking command overrides (keep event loop responsive) + # ----------------------------------------------------------------- + + async def _cmd_sync_next_message(self, data: bytes) -> None: + """Sync next message; run persistence read in thread so SQLite does not block.""" + msg = self.bridge.sync_next_message() + if msg is None: + msg = await asyncio.to_thread(self._sync_next_from_persistence) + if msg is None: + self._write_frame(bytes([RESP_CODE_NO_MORE_MESSAGES])) + return + if msg.is_channel: + path_len_byte = msg.path_len if msg.path_len < 256 else 0xFF + txt_type = 0 + text_bytes = (msg.text or "").rstrip("\x00").encode("utf-8", errors="replace") + if self._app_target_ver >= 3: + frame = ( + bytes( + [ + RESP_CODE_CHANNEL_MSG_RECV_V3, + 0, + 0, + 0, + msg.channel_idx, + path_len_byte, + txt_type, + ] + ) + + struct.pack("= 6 else msg.sender_key.ljust(6, b"\x00") + ) + path_len_byte = msg.path_len if msg.path_len < 256 else 0xFF + text_bytes = msg.text.encode("utf-8", errors="replace") + if self._app_target_ver >= 3: + frame = ( + bytes([RESP_CODE_CONTACT_MSG_RECV_V3, 0, 0, 0]) + + prefix + + bytes([path_len_byte, msg.txt_type]) + + struct.pack(" dict: """Convert a Contact object to a persistence dict.""" diff --git a/repeater/main.py b/repeater/main.py index a4a87d7..bd15856 100644 --- a/repeater/main.py +++ b/repeater/main.py @@ -651,7 +651,7 @@ class RepeaterDaemon: return stats - def _get_companion_stats(self, stats_type: int) -> dict: + async def _get_companion_stats(self, stats_type: int) -> dict: """Return stats dict for companion CMD_GET_STATS (format expected by frame_server + meshcore_py).""" from repeater.companion.constants import ( STATS_TYPE_CORE,