From 3de5e68e6808d41f917e89528def953738dadbcc Mon Sep 17 00:00:00 2001 From: ribs Date: Sat, 28 Feb 2026 14:11:04 -0800 Subject: [PATCH] fix: improve pager message display and encryption classification Three issues caused POCSAG messages to be incorrectly hidden or misclassified in the Device Intelligence panel: 1. detectEncryption used a narrow character class ([a-zA-Z0-9\s.,!?-]) to measure "printable ratio". Messages containing common printable ASCII characters like : = / + @ fell below the 0.8 threshold and returned null ("Unknown") instead of false ("Plaintext"). Simplified to check all printable ASCII (\x20-\x7E) which correctly classifies base64, structured data, and punctuation-heavy content. 2. The default hideToneOnly filter was true, hiding all address-only (Tone) pager messages. When RF conditions cause multimon-ng to decode the address but not the message content, the resulting Tone card was silently filtered. Changed default to false so users see all traffic and can opt-in to filtering. 3. The multimon-ng output parser only recognized "Alpha" and "Numeric" content type labels. Added a catch-all pattern to capture any additional content type labels that future multimon-ng versions or forks might emit, rather than dropping them to raw output. Co-Authored-By: Claude Opus 4.6 --- routes/pager.py | 14 ++++++++++++++ templates/index.html | 23 ++++++++++++----------- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/routes/pager.py b/routes/pager.py index 7777ced..3c53d7a 100644 --- a/routes/pager.py +++ b/routes/pager.py @@ -55,6 +55,20 @@ def parse_multimon_output(line: str) -> dict[str, str] | None: 'message': pocsag_match.group(5).strip() or '[No Message]' } + # POCSAG parsing - other content types (catch-all for non-Alpha/Numeric labels) + pocsag_other_match = re.match( + r'(POCSAG\d+):\s*Address:\s*(\d+)\s+Function:\s*(\d+)\s+(\w+):\s*(.*)', + line + ) + if pocsag_other_match: + return { + 'protocol': pocsag_other_match.group(1), + 'address': pocsag_other_match.group(2), + 'function': pocsag_other_match.group(3), + 'msg_type': pocsag_other_match.group(4), + 'message': pocsag_other_match.group(5).strip() or '[No Message]' + } + # POCSAG parsing - address only (no message content) pocsag_addr_match = re.match( r'(POCSAG\d+):\s*Address:\s*(\d+)\s+Function:\s*(\d+)\s*$', diff --git a/templates/index.html b/templates/index.html index 9d5cbf8..f4493df 100644 --- a/templates/index.html +++ b/templates/index.html @@ -3611,7 +3611,7 @@ // Pager message filter settings let pagerFilters = { - hideToneOnly: true, + hideToneOnly: false, keywords: [] }; @@ -6911,20 +6911,21 @@ return null; // Can't determine } - // Check for high entropy (random-looking data) - const printableRatio = (message.match(/[a-zA-Z0-9\s.,!?-]/g) || []).length / message.length; - - // Check for common encrypted patterns (hex strings, base64-like) - const hexPattern = /^[0-9A-Fa-f\s]+$/; + // Check for non-printable characters (outside printable ASCII range) const hasNonPrintable = /[^\x20-\x7E]/.test(message); - if (printableRatio > 0.8 && !hasNonPrintable) { - return false; // Likely plaintext - } else if (hexPattern.test(message.replace(/\s/g, '')) || hasNonPrintable) { - return true; // Likely encrypted or encoded + // Check for common encrypted patterns (hex strings) + const hexPattern = /^[0-9A-Fa-f\s]+$/; + + if (hasNonPrintable) { + return true; // Contains non-printable chars — likely encrypted or encoded + } + if (hexPattern.test(message.replace(/\s/g, ''))) { + return true; // Pure hex data — likely encoded } - return null; // Unknown + // All printable ASCII (covers base64, structured data, punctuation, etc.) + return false; // Likely plaintext } // Generate device fingerprint