From d9119d3b92879d14bc7e78b0e4af610675a678af Mon Sep 17 00:00:00 2001 From: Lloyd Date: Sat, 20 Dec 2025 22:34:42 +0000 Subject: [PATCH] Refactor text message handling to prioritize CLI command processing before storing room server messages --- repeater/handler_helpers/text.py | 88 ++++++++++++++++---------------- 1 file changed, 45 insertions(+), 43 deletions(-) diff --git a/repeater/handler_helpers/text.py b/repeater/handler_helpers/text.py index 82e528b..964e1fb 100644 --- a/repeater/handler_helpers/text.py +++ b/repeater/handler_helpers/text.py @@ -9,6 +9,7 @@ Also handles CLI commands for admin users on the repeater identity. import asyncio import logging import struct +import time from pymc_core.node.handlers.text import TextMessageHandler from .mesh_cli import MeshCLI @@ -16,6 +17,10 @@ from .room_server import RoomServer logger = logging.getLogger("TextHelper") +# Text message type flags +TXT_TYPE_PLAIN = 0x00 +TXT_TYPE_CLI_DATA = 0x01 + class TextHelper: @@ -240,45 +245,13 @@ class TextHelper: f"[{identity_type}:{identity_name}] Message: {message_text}" ) - # Handle room server messages - store to database + # Handle room server messages if identity_type == "room_server" and dest_hash in self.room_servers: - try: - room_server = self.room_servers[dest_hash] - - # Get sender's full public key from ACL - identity_acl = self.acl_dict.get(dest_hash) - sender_pubkey = bytes([src_hash]) + b'\x00' * 31 # Default - if identity_acl: - for client_info in identity_acl.get_all_clients(): - if client_info.id.get_public_key()[0] == src_hash: - sender_pubkey = client_info.id.get_public_key() - break - - # Extract timestamp and txt_type from decrypted data - # Packet decryption already happened in TextMessageHandler - # We need to extract from original payload if available - sender_timestamp = int(packet.decrypted.get('timestamp', 0)) if hasattr(packet, 'decrypted') else 0 - txt_type = 0 # TXT_TYPE_PLAIN by default - - # Store message to room database - # SECURITY: Radio messages cannot use server author key - await room_server.add_post( - client_pubkey=sender_pubkey, - message_text=message_text, - sender_timestamp=sender_timestamp, - txt_type=txt_type, - allow_server_author=False # Block server key from radio - ) - - logger.info( - f"Room '{identity_name}': Stored message from 0x{src_hash:02X}" - ) - except Exception as e: - logger.error(f"Failed to store room message: {e}", exc_info=True) + room_server = self.room_servers[dest_hash] - # Check if this is a CLI command to the room server + # Check if this is a CLI command FIRST (before storing as post) if self._is_cli_command(message_text): - room_server = self.room_servers.get(dest_hash) + # Handle CLI command - do NOT store as post if room_server and room_server.cli: try: # Check admin permission @@ -313,8 +286,36 @@ class TextHelper: except Exception as e: logger.error(f"Error processing room server CLI command: {e}", exc_info=True) + + # CLI command handled, don't store as post + return + + # NOT a CLI command - store as regular room post + try: + # Get sender's full pubkey + identity_acl = self.acl_dict.get(dest_hash) + sender_pubkey = bytes([src_hash]) + b'\x00' * 31 # Default + if identity_acl: + for client_info in identity_acl.get_all_clients(): + if client_info.id.get_public_key()[0] == src_hash: + sender_pubkey = client_info.id.get_public_key() + break + + # Store message as post + sender_timestamp = int(time.time()) + success = await room_server.add_post( + client_pubkey=sender_pubkey, + message_text=message_text, + sender_timestamp=sender_timestamp, + txt_type=TXT_TYPE_PLAIN + ) + + if success: + logger.info(f"Room '{identity_name}': New post from {sender_pubkey[:4].hex()}: {message_text[:50]}") + + except Exception as e: + logger.error(f"Error storing room post: {e}", exc_info=True) - # Room messages (non-CLI) don't need further processing return # Check if this is a CLI command to the repeater (AFTER decryption) @@ -453,21 +454,22 @@ class TextHelper: incoming_route = original_packet.get_route_type() logger.debug(f"CLI reply: original packet dest=0x{dest_hash:02X}, src=0x{src_hash:02X}, incoming_route={incoming_route}") - # Find the client in repeater's ACL to get shared secret - repeater_acl = self.acl_dict.get(self.repeater_hash) - if not repeater_acl: - logger.error("No repeater ACL found for CLI reply") + # Find the client in the DESTINATION identity's ACL (not always repeater!) + # dest_hash is the identity that received the command (repeater OR room server) + identity_acl = self.acl_dict.get(dest_hash) + if not identity_acl: + logger.error(f"No ACL found for identity 0x{dest_hash:02X} for CLI reply") return client = None - for client_info in repeater_acl.get_all_clients(): + for client_info in identity_acl.get_all_clients(): pubkey = client_info.id.get_public_key() if pubkey[0] == src_hash: client = client_info break if not client: - logger.error(f"Client 0x{src_hash:02X} not found in ACL for CLI reply") + logger.error(f"Client 0x{src_hash:02X} not found in identity 0x{dest_hash:02X} ACL for CLI reply") return # Get shared secret from client