"""Pure formatting utilities for LoRa-friendly text. These helpers have NO dependency on the Hermes gateway package so they can be imported and unit-tested in isolation (CI runs them without hermes-agent). """ import re MAX_LORA_MESSAGE_LENGTH = 180 # Characters per packet to prevent LoRa airtime congestion _MARKDOWN_RULES = ( (r"\*\*(.+?)\*\*", r"\1"), # bold (r"__(.+?)__", r"\1"), (r"\*(.+?)\*", r"\1"), # italic (r"(? str: """Convert markdown formatting into plain readable text suitable for radio.""" if not text: return "" result = text for pattern, replacement in _MARKDOWN_RULES: result = re.sub(pattern, replacement, result, flags=re.MULTILINE) # Collapse multiple blank lines result = re.sub(r"\n{3,}", "\n\n", result) return result.strip() def chunk_text(text: str, limit: int = MAX_LORA_MESSAGE_LENGTH) -> list: """Split text into smaller chunks for LoRa transmission, breaking at whitespace/newlines.""" if not text: return [] if len(text) <= limit: return [text] chunks = [] remaining = text while remaining: if len(remaining) <= limit: chunks.append(remaining.strip()) break split_at = remaining.rfind(" ", 0, limit) if split_at <= 0 or split_at < (limit // 3): newline_at = remaining.rfind("\n", 0, limit) if newline_at > 0: split_at = newline_at else: split_at = limit chunk = remaining[:split_at].strip() if chunk: chunks.append(chunk) remaining = remaining[split_at:].lstrip() return chunks