From 33db12b971deeddf7b63d3e96fb5123a355b7986 Mon Sep 17 00:00:00 2001 From: Ivan Date: Tue, 14 Apr 2026 19:31:48 -0500 Subject: [PATCH] refactor(code): clean up import statements, improve docstrings, and update error handling across various modules --- meshchatx/src/backend/announce_manager.py | 18 ++++++++++ meshchatx/src/backend/async_utils.py | 7 ++-- meshchatx/src/backend/database/__init__.py | 19 ++++++----- .../src/backend/database/access_attempts.py | 14 ++++++-- .../src/backend/database/legacy_migrator.py | 7 ++-- meshchatx/src/backend/database/schema.py | 14 ++++---- meshchatx/src/backend/database/stickers.py | 9 ++--- meshchatx/src/backend/integrity_manager.py | 15 +++++---- meshchatx/src/backend/lxmf_utils.py | 9 +++-- meshchatx/src/backend/meshchat_utils.py | 8 ++--- meshchatx/src/backend/page_node.py | 32 +++++++++++++----- meshchatx/src/backend/page_node_manager.py | 3 +- .../src/backend/persistent_log_handler.py | 2 +- .../src/backend/recovery/crash_recovery.py | 33 +++++++++---------- .../src/backend/recovery/health_monitor.py | 13 ++++---- meshchatx/src/backend/rncp_handler.py | 2 +- meshchatx/src/backend/rnstatus_handler.py | 6 ++-- meshchatx/src/backend/sticker_utils.py | 19 +++++------ meshchatx/src/backend/telephone_manager.py | 2 +- meshchatx/src/backend/voicemail_manager.py | 2 +- meshchatx/src/path_utils.py | 1 + meshchatx/src/version.py | 5 +-- 22 files changed, 142 insertions(+), 98 deletions(-) diff --git a/meshchatx/src/backend/announce_manager.py b/meshchatx/src/backend/announce_manager.py index e697c16..a8c6a2f 100644 --- a/meshchatx/src/backend/announce_manager.py +++ b/meshchatx/src/backend/announce_manager.py @@ -177,3 +177,21 @@ class AnnounceManager: result = self.db.provider.fetchone(sql, params) return result["count"] if result else 0 + + +def filter_announced_dicts_by_search_query( + items: list[dict], + search_query: str, +) -> list[dict]: + """Case-insensitive substring match on display name, hashes, and custom display name.""" + q = search_query.lower() + return [ + a + for a in items + if ( + (a.get("display_name") and q in a["display_name"].lower()) + or (a.get("destination_hash") and q in a["destination_hash"].lower()) + or (a.get("identity_hash") and q in a["identity_hash"].lower()) + or (a.get("custom_display_name") and q in a["custom_display_name"].lower()) + ) + ] diff --git a/meshchatx/src/backend/async_utils.py b/meshchatx/src/backend/async_utils.py index e33ad3b..686028e 100644 --- a/meshchatx/src/backend/async_utils.py +++ b/meshchatx/src/backend/async_utils.py @@ -13,9 +13,10 @@ class AsyncUtils: @staticmethod def apply_asyncio_313_patch(): - """Apply a patch for asyncio on Python 3.13 to avoid a bug in sendfile with SSL. - See: https://github.com/python/cpython/issues/124448 - And: https://github.com/aio-libs/aiohttp/issues/8863 + """Patch asyncio on Python 3.13 to avoid sendfile + SSL failures. + + See https://github.com/python/cpython/issues/124448 and + https://github.com/aio-libs/aiohttp/issues/8863. """ if sys.version_info >= (3, 13): import asyncio.base_events diff --git a/meshchatx/src/backend/database/__init__.py b/meshchatx/src/backend/database/__init__.py index 2b1f574..ea88330 100644 --- a/meshchatx/src/backend/database/__init__.py +++ b/meshchatx/src/backend/database/__init__.py @@ -163,9 +163,9 @@ class Database: return False def check_db_health_at_open(self, storage_path): - """ - Run integrity and baseline checks after opening the database. - Returns a list of human-readable issue strings; empty if healthy. + """Run integrity and baseline checks after opening the database. + + Returns human-readable issue strings; empty if healthy. """ issues = [] try: @@ -210,9 +210,9 @@ class Database: return issues def check_db_health_at_close(self, storage_path): - """ - Run health checks before closing the database (for logging only). - Returns a list of issue strings; empty if healthy. + """Run health checks before closing the database (for logging only). + + Returns issue strings; empty if healthy. """ issues = [] try: @@ -234,7 +234,7 @@ class Database: baseline = self._read_backup_baseline(storage_path) if self._is_backup_suspicious(current, baseline): issues.append( - "Database content anomaly detected at close. Consider restoring from backup." + "Database content anomaly detected at close. Consider restoring from backup.", ) _log.warning("DB close health check: content anomaly") else: @@ -392,7 +392,8 @@ class Database: if backup_path is None: if suspicious: backup_path = os.path.join( - default_dir, f"backup-SUSPICIOUS-{timestamp}.zip" + default_dir, + f"backup-SUSPICIOUS-{timestamp}.zip", ) else: backup_path = os.path.join(default_dir, f"backup-{timestamp}.zip") @@ -412,7 +413,7 @@ class Database: "Backup data-loss guard: current database looks wrong " f"(was {baseline.get('message_count')} messages / {baseline.get('total_bytes')} bytes, " f"now {current_stats.get('message_count')} / {current_stats.get('total_bytes')}). " - "Wrote backup-SUSPICIOUS-*.zip; skipping rotation and baseline update. Check disk and DB." + "Wrote backup-SUSPICIOUS-*.zip; skipping rotation and baseline update. Check disk and DB.", ) result["suspicious"] = True result["baseline"] = baseline diff --git a/meshchatx/src/backend/database/access_attempts.py b/meshchatx/src/backend/database/access_attempts.py index 21b5136..14b6418 100644 --- a/meshchatx/src/backend/database/access_attempts.py +++ b/meshchatx/src/backend/database/access_attempts.py @@ -114,7 +114,10 @@ class AccessAttemptsDAO: ) def count_login_attempts_ip( - self, client_ip: str, path: str, since_ts: float + self, + client_ip: str, + path: str, + since_ts: float, ) -> int: row = self.provider.fetchone( """ @@ -142,7 +145,10 @@ class AccessAttemptsDAO: return int(row["c"]) if row else 0 def count_lockout_failures( - self, identity_hash: str, client_ip: str, since_ts: float + self, + identity_hash: str, + client_ip: str, + since_ts: float, ) -> int: row = self.provider.fetchone( """ @@ -190,7 +196,9 @@ class AccessAttemptsDAO: return [dict(r) for r in rows] def count_attempts( - self, search: str | None = None, outcome: str | None = None + self, + search: str | None = None, + outcome: str | None = None, ) -> int: sql = "SELECT COUNT(*) AS c FROM access_attempts WHERE 1=1" params: list[Any] = [] diff --git a/meshchatx/src/backend/database/legacy_migrator.py b/meshchatx/src/backend/database/legacy_migrator.py index 596fa50..4eed978 100644 --- a/meshchatx/src/backend/database/legacy_migrator.py +++ b/meshchatx/src/backend/database/legacy_migrator.py @@ -44,8 +44,9 @@ class LegacyMigrator: return None def should_migrate(self): - """Check if migration should be performed. - Only migrates if the current database is empty and a legacy database exists. + """Return whether migration should run. + + Only migrates when the current database is empty and a legacy DB exists. """ legacy_path = self.get_legacy_db_path() if not legacy_path: @@ -77,7 +78,7 @@ class LegacyMigrator: # We use a randomized alias to avoid collisions alias = f"legacy_{os.urandom(4).hex()}" safe_path = legacy_path.replace("'", "''") - self.provider.execute(f"ATTACH DATABASE '{safe_path}' AS {alias}") # noqa: S608 + self.provider.execute(f"ATTACH DATABASE '{safe_path}' AS {alias}") # Tables that existed in the legacy Peewee version tables_to_migrate = [ diff --git a/meshchatx/src/backend/database/schema.py b/meshchatx/src/backend/database/schema.py index 28cf8bc..cbf4366 100644 --- a/meshchatx/src/backend/database/schema.py +++ b/meshchatx/src/backend/database/schema.py @@ -44,7 +44,7 @@ class DatabaseSchema: cursor = self.provider.connection.cursor() try: - cursor.execute(f"PRAGMA table_info({table_name})") # noqa: S608 + cursor.execute(f"PRAGMA table_info({table_name})") columns = [row[1] for row in cursor.fetchall()] finally: cursor.close() @@ -67,7 +67,7 @@ class DatabaseSchema: ).strip() res = self._safe_execute( - f"ALTER TABLE {table_name} ADD COLUMN {column_name} {stmt_type}", # noqa: S608 + f"ALTER TABLE {table_name} ADD COLUMN {column_name} {stmt_type}", ) return res is not None except Exception as e: @@ -77,13 +77,14 @@ class DatabaseSchema: ) return False return True - return True def _sync_table_columns(self, table_name, create_sql): - """Parses a CREATE TABLE statement and ensures all columns exist in the actual table. - This is a robust way to handle legacy tables that are missing columns. + """Parse CREATE TABLE and add any missing columns to match the declaration. + + Finds the column list between the first ``(`` and last ``)``, splits on + commas outside nested parentheses (e.g. ``DECIMAL(10,2)``), then ensures + each column exists on the actual table. """ - # Find the first '(' and the last ')' start_idx = create_sql.find("(") end_idx = create_sql.rfind(")") @@ -92,7 +93,6 @@ class DatabaseSchema: inner_content = create_sql[start_idx + 1 : end_idx] - # Split by comma but ignore commas inside parentheses (e.g. DECIMAL(10,2)) definitions = [] depth = 0 current = "" diff --git a/meshchatx/src/backend/database/stickers.py b/meshchatx/src/backend/database/stickers.py index ee5744e..ba31920 100644 --- a/meshchatx/src/backend/database/stickers.py +++ b/meshchatx/src/backend/database/stickers.py @@ -54,7 +54,10 @@ class UserStickersDAO: return cur.rowcount def update_name( - self, sticker_id: int, identity_hash: str, name: str | None + self, + sticker_id: int, + identity_hash: str, + name: str | None, ) -> bool: now = time.time() cur = self.provider.execute( @@ -75,9 +78,7 @@ class UserStickersDAO: image_bytes: bytes, source_message_hash: str | None = None, ) -> dict | None: - """ - Insert a sticker. Returns summary dict or None if duplicate (same content_hash). - """ + """Insert a sticker. Returns summary dict or None if duplicate (same content_hash).""" if ( self.count_for_identity(identity_hash) >= sticker_utils.MAX_STICKERS_PER_IDENTITY diff --git a/meshchatx/src/backend/integrity_manager.py b/meshchatx/src/backend/integrity_manager.py index 811a2d7..819636c 100644 --- a/meshchatx/src/backend/integrity_manager.py +++ b/meshchatx/src/backend/integrity_manager.py @@ -159,11 +159,11 @@ class IntegrityManager: and abs(actual_entropy - saved_entropy) > 1.0 ): issues.append( - f"Database structural anomaly (Entropy Δ: {abs(actual_entropy - saved_entropy):.2f})" + f"Database structural anomaly (Entropy Δ: {abs(actual_entropy - saved_entropy):.2f})", ) else: issues.append( - f"Database binary signature mismatch: {db_rel}" + f"Database binary signature mismatch: {db_rel}", ) # Check other critical files in storage_dir @@ -185,10 +185,11 @@ class IntegrityManager: if actual_hash != manifest_files[rel_path]: actual_entropy = self._calculate_entropy(full_path) saved_entropy = manifest_metadata.get(rel_path, {}).get( - "entropy" + "entropy", ) saved_size = manifest_metadata.get(rel_path, {}).get( - "size", 0 + "size", + 0, ) actual_size = full_path.stat().st_size @@ -198,18 +199,18 @@ class IntegrityManager: if is_critical: issues.append( - f"Critical security component integrity compromised: {rel_path}" + f"Critical security component integrity compromised: {rel_path}", ) elif ( saved_entropy is not None and abs(actual_entropy - saved_entropy) > 1.5 ): issues.append( - f"Non-linear content shift detected in {rel_path} (Entropy Δ: {abs(actual_entropy - saved_entropy):.2f})" + f"Non-linear content shift detected in {rel_path} (Entropy Δ: {abs(actual_entropy - saved_entropy):.2f})", ) elif saved_size and actual_size != saved_size: issues.append( - f"File size divergence: {rel_path} ({saved_size} -> {actual_size} bytes)" + f"File size divergence: {rel_path} ({saved_size} -> {actual_size} bytes)", ) else: issues.append(f"File signature mismatch: {rel_path}") diff --git a/meshchatx/src/backend/lxmf_utils.py b/meshchatx/src/backend/lxmf_utils.py index 3f337f8..7c3d046 100644 --- a/meshchatx/src/backend/lxmf_utils.py +++ b/meshchatx/src/backend/lxmf_utils.py @@ -401,12 +401,11 @@ def convert_db_lxmf_message_to_dict( def compute_lxmf_conversation_unread_from_latest_row(row): - """ - Whether the conversation list should show unread for this latest-message row, - using lxmf_conversation_read_state.last_read_at only. + """Return whether the conversation row should appear as unread. - Latest message must be incoming to be unread; if the last message is ours, - the thread is not unread (matches filter_unread SQL in MessageHandler.get_conversations). + Uses ``lxmf_conversation_read_state.last_read_at`` only. The latest message + must be incoming; outbound-only threads are not unread (matches + ``filter_unread`` in ``MessageHandler.get_conversations``). """ from datetime import UTC, datetime diff --git a/meshchatx/src/backend/meshchat_utils.py b/meshchatx/src/backend/meshchat_utils.py index 1580ef3..0fcd9a2 100644 --- a/meshchatx/src/backend/meshchat_utils.py +++ b/meshchatx/src/backend/meshchat_utils.py @@ -10,15 +10,15 @@ from LXMF import LXMRouter def create_lxmf_router(identity, storagepath, propagation_cost=None): - """Creates an LXMF.LXMRouter instance safely, avoiding signal handler crashes - when called from non-main threads. + """Construct an ``LXMF.LXMRouter`` without signal-handler crashes off the main thread. + + ``signal.signal`` only works on the main thread; on workers it is temporarily + replaced with a no-op while the router is created. """ if propagation_cost is None: propagation_cost = 0 if threading.current_thread() != threading.main_thread(): - # signal.signal can only be called from the main thread in Python - # We monkeypatch it temporarily to avoid the ValueError original_signal = signal.signal try: signal.signal = lambda s, h: None diff --git a/meshchatx/src/backend/page_node.py b/meshchatx/src/backend/page_node.py index e23a71a..d8af347 100644 --- a/meshchatx/src/backend/page_node.py +++ b/meshchatx/src/backend/page_node.py @@ -1,5 +1,4 @@ -""" -PageNode: Serves Micron pages and files over RNS. +"""PageNode: Serves Micron pages and files over RNS. Each PageNode owns an RNS Destination (SINGLE, IN) with the aspect nomadnetwork.node and registers per-page request handlers at @@ -19,7 +18,6 @@ import time import RNS - APP_NAME = "nomadnetwork" ASPECT = "node" DEFAULT_INDEX = "index.mu" @@ -58,6 +56,19 @@ def _safe_mesh_file_basename(name: str) -> str: return base +def _reject_name_component_too_long(parent_dir: str, component: str) -> None: + """Raise ValueError if basename exceeds this directory's filename length limit.""" + try: + if parent_dir and os.path.isdir(parent_dir): + max_bytes = int(os.pathconf(parent_dir, "PC_NAME_MAX")) + else: + max_bytes = 255 + except (OSError, ValueError, TypeError, OverflowError): + max_bytes = 255 + if len(os.fsencode(component)) > max_bytes: + raise ValueError("name too long") + + class PageNode: """A single page-serving node on the Reticulum mesh.""" @@ -163,9 +174,9 @@ class PageNode: self.active_links.remove(link) def _ensure_local_path(self): - """ - Register the destination's identity in RNS.Identity.known_destinations - so that Identity.recall() can resolve it for local link establishment. + """Register this identity in ``RNS.Identity.known_destinations``. + + Lets ``Identity.recall()`` resolve the destination for local link setup. """ if not self.destination: return @@ -292,6 +303,7 @@ class PageNode: def add_page(self, name, content): """Write a page file and register its request handler.""" name = normalize_page_filename(name) + _reject_name_component_too_long(self.pages_dir, name) page_path = os.path.join(self.pages_dir, name) if isinstance(content, str): content = content.encode("utf-8") @@ -305,6 +317,7 @@ class PageNode: """Remove a page and deregister its request handler.""" try: name = normalize_page_filename(name) + _reject_name_component_too_long(self.pages_dir, name) except ValueError: return False page_path = os.path.join(self.pages_dir, name) @@ -329,17 +342,19 @@ class PageNode: """Read and return a page's content.""" try: name = normalize_page_filename(name) + _reject_name_component_too_long(self.pages_dir, name) except ValueError: return None page_path = os.path.join(self.pages_dir, name) if not os.path.isfile(page_path): return None - with open(page_path, "r", encoding="utf-8") as f: + with open(page_path, encoding="utf-8") as f: return f.read() def add_file(self, name, data): """Write a file and register its request handler.""" name = _safe_mesh_file_basename(name) + _reject_name_component_too_long(self.files_dir, name) file_path = os.path.join(self.files_dir, name) mode = "wb" if isinstance(data, bytes) else "w" with open(file_path, mode) as f: @@ -352,6 +367,7 @@ class PageNode: """Remove a file and deregister its request handler.""" try: name = _safe_mesh_file_basename(name) + _reject_name_component_too_long(self.files_dir, name) except ValueError: return False file_path = os.path.join(self.files_dir, name) @@ -413,5 +429,5 @@ class PageNode: config_path = os.path.join(base_dir, "config.json") if not os.path.isfile(config_path): return None - with open(config_path, "r") as f: + with open(config_path) as f: return json.load(f) diff --git a/meshchatx/src/backend/page_node_manager.py b/meshchatx/src/backend/page_node_manager.py index 665a143..9a85b4d 100644 --- a/meshchatx/src/backend/page_node_manager.py +++ b/meshchatx/src/backend/page_node_manager.py @@ -1,5 +1,4 @@ -""" -PageNodeManager: Manages the lifecycle of multiple PageNode instances. +"""PageNodeManager: Manages the lifecycle of multiple PageNode instances. Handles creation, deletion, persistence, start/stop, and announce scheduling for page nodes. Each node gets its own subdirectory under diff --git a/meshchatx/src/backend/persistent_log_handler.py b/meshchatx/src/backend/persistent_log_handler.py index 6260d63..49d7f16 100644 --- a/meshchatx/src/backend/persistent_log_handler.py +++ b/meshchatx/src/backend/persistent_log_handler.py @@ -250,7 +250,7 @@ class PersistentLogHandler(logging.Handler): """Shannon entropy over log-level distribution in the last 60 seconds.""" cutoff = time.monotonic() - 60.0 with self.lock: - counts = {lv: 0 for lv in _LOG_LEVELS} + counts = dict.fromkeys(_LOG_LEVELS, 0) total = 0 for ts, level in self._level_events: if ts >= cutoff: diff --git a/meshchatx/src/backend/recovery/crash_recovery.py b/meshchatx/src/backend/recovery/crash_recovery.py index b00309c..bf47a12 100644 --- a/meshchatx/src/backend/recovery/crash_recovery.py +++ b/meshchatx/src/backend/recovery/crash_recovery.py @@ -1,10 +1,7 @@ -"""CRASH RECOVERY & ADAPTIVE DIAGNOSTIC ENGINE --------------------------------------------------- -Diagnostic system for MeshChatX. +"""Crash recovery and adaptive diagnostics for MeshChatX. -Uses Shannon Entropy, KL-Divergence, and Bayesian weight learning -to diagnose application failures. Crash history is persisted and -priors are refined over time using a conjugate Beta-Binomial model. +Uses entropy, KL-divergence, and Bayesian weight learning. Crash history is +persisted; priors refine over time (conjugate Beta-Binomial model). """ import contextlib @@ -36,9 +33,9 @@ _DEFAULT_PRIORS = { class CrashRecovery: - """A diagnostic utility that intercepts application crashes and provides - meaningful error reports and system state analysis. Learns from crash - history to refine root-cause probabilities over time. + """Intercept crashes and report diagnostics plus environment state. + + Learns from crash history to refine root-cause probabilities over time. """ def __init__( @@ -115,7 +112,13 @@ class CrashRecovery: return _DEFAULT_PRIORS.get(cause_key, 0.05) def _persist_crash( - self, error_type, error_msg, causes, symptoms, entropy, divergence + self, + error_type, + error_msg, + causes, + symptoms, + entropy, + divergence, ): """Store crash event in crash_history for future learning.""" if not self.database: @@ -271,9 +274,7 @@ class CrashRecovery: sys.exit(1) def _analyze_cause(self, exc_type, exc_value, diagnosis): - """Uses heuristic pattern matching and Bayesian priors - to determine the likely root cause of the application crash. - """ + """Rank likely root causes using heuristics and Bayesian priors.""" causes = [] error_msg = str(exc_value).lower() error_type = exc_type.__name__.lower() @@ -395,7 +396,7 @@ class CrashRecovery: or (py_version.major == 3 and py_version.minor < 10), "legacy_kernel": "linux" in platform.system().lower() and (lambda m: m is not None and float(m.group(1)) < 4.0)( - re.search(r"(\d+\.\d+)", platform.release()) + re.search(r"(\d+\.\d+)", platform.release()), ), "attribute_error": "attributeerror" in error_type, } @@ -467,9 +468,7 @@ class CrashRecovery: return causes def _calculate_system_entropy(self, diagnosis): - """Calculates a heuristic system state entropy and KL-Divergence. - Provides a mathematical measure of both disorder and 'surprise' (Information Gain). - """ + """Return heuristic system entropy and KL-divergence (information gain).""" import math def h(p): diff --git a/meshchatx/src/backend/recovery/health_monitor.py b/meshchatx/src/backend/recovery/health_monitor.py index 748143a..c874283 100644 --- a/meshchatx/src/backend/recovery/health_monitor.py +++ b/meshchatx/src/backend/recovery/health_monitor.py @@ -81,7 +81,7 @@ class HealthMonitor: "kind": "entropy_climbing", "message": f"Log entropy rising: {entropy:.2f} bits (threshold {self.ENTROPY_WARN_THRESHOLD})", "value": round(entropy, 4), - } + }, ) if self._consecutive_above(self._error_rate_history, self.ERROR_RATE_WARN): @@ -90,7 +90,7 @@ class HealthMonitor: "kind": "error_rate_high", "message": f"Error rate elevated: {error_rate:.0%}", "value": round(error_rate, 4), - } + }, ) if self._consecutive_below(self._mem_available_history, self.MEMORY_WARN_MB): @@ -99,7 +99,7 @@ class HealthMonitor: "kind": "memory_low", "message": f"Available memory low: {available_mb:.0f} MB", "value": round(available_mb, 1), - } + }, ) for w in warnings: @@ -107,8 +107,7 @@ class HealthMonitor: self._broadcast(w) def _detect_entropy_climb(self): - """True if 3+ consecutive entropy readings are strictly increasing - AND the latest exceeds the warning threshold.""" + """Return True when entropy climbs in 3+ steps above the warn threshold.""" h = self._entropy_history if len(h) < 3: return False @@ -140,8 +139,8 @@ class HealthMonitor: AsyncUtils.run_async( self.app.websocket_broadcast( - json.dumps({"type": "health_warning", "data": warning_data}) - ) + json.dumps({"type": "health_warning", "data": warning_data}), + ), ) except Exception: pass diff --git a/meshchatx/src/backend/rncp_handler.py b/meshchatx/src/backend/rncp_handler.py index 7c5a56c..6d7a372 100644 --- a/meshchatx/src/backend/rncp_handler.py +++ b/meshchatx/src/backend/rncp_handler.py @@ -227,7 +227,7 @@ class RNCPHandler: if data.startswith(self.fetch_jail + "/"): data = data.replace(self.fetch_jail + "/", "") file_path = os.path.realpath( - os.path.expanduser(f"{self.fetch_jail}/{data}") + os.path.expanduser(f"{self.fetch_jail}/{data}"), ) jail_real = os.path.realpath(self.fetch_jail) if not file_path.startswith(jail_real + "/"): diff --git a/meshchatx/src/backend/rnstatus_handler.py b/meshchatx/src/backend/rnstatus_handler.py index a49d19b..3db5889 100644 --- a/meshchatx/src/backend/rnstatus_handler.py +++ b/meshchatx/src/backend/rnstatus_handler.py @@ -274,15 +274,15 @@ class RNStatusHandler: if "incoming_announce_frequency" in ifstat: formatted_if["incoming_announce_frequency"] = fmt_per_second( - ifstat["incoming_announce_frequency"] + ifstat["incoming_announce_frequency"], ) if "outgoing_announce_frequency" in ifstat: formatted_if["outgoing_announce_frequency"] = fmt_per_second( - ifstat["outgoing_announce_frequency"] + ifstat["outgoing_announce_frequency"], ) if "held_announces" in ifstat: formatted_if["held_announces"] = fmt_packet_count( - ifstat["held_announces"] + ifstat["held_announces"], ) if "ifac_netname" in ifstat and ifstat["ifac_netname"] is not None: diff --git a/meshchatx/src/backend/sticker_utils.py b/meshchatx/src/backend/sticker_utils.py index 0891507..28dd0bb 100644 --- a/meshchatx/src/backend/sticker_utils.py +++ b/meshchatx/src/backend/sticker_utils.py @@ -2,8 +2,8 @@ from __future__ import annotations -import hashlib import base64 +import hashlib MAX_STICKER_BYTES = 512 * 1024 MAX_STICKERS_PER_IDENTITY = 2000 @@ -35,9 +35,9 @@ def content_hash_hex(image_bytes: bytes) -> str: def detect_image_format_from_magic(image_bytes: bytes) -> str | None: - """ - Identify image format from file signature (magic bytes). Returns normalized - type key (png, jpeg, gif, webp, bmp) or None if unknown / too short / not allowed. + """Detect image format from magic bytes. + + Returns a normalized type key (png, jpeg, gif, webp, bmp), or None. """ if not isinstance(image_bytes, (bytes, bytearray)) or len(image_bytes) < 4: return None @@ -59,8 +59,7 @@ def validate_sticker_payload( image_bytes: bytes, image_type: str | None, ) -> tuple[str, str]: - """ - Returns (normalized_image_type, content_hash_hex). + """Returns (normalized_image_type, content_hash_hex). Declared image_type must match the format detected from magic bytes; stored type is the normalized detected format. @@ -99,10 +98,10 @@ _EXPORT_VERSION = 1 def validate_export_document(data: object) -> list[dict]: - """ - Parse and lightly validate an import JSON document. - Returns a list of sticker dicts with keys: name, image_type, image_bytes (str base64), - source_message_hash (optional). + """Parse and validate a sticker export JSON document. + + Each sticker dict has ``name``, ``image_type``, ``image_bytes`` (base64), and + optional ``source_message_hash``. """ if not isinstance(data, dict): msg = "invalid_document" diff --git a/meshchatx/src/backend/telephone_manager.py b/meshchatx/src/backend/telephone_manager.py index 4320c67..79c3e37 100644 --- a/meshchatx/src/backend/telephone_manager.py +++ b/meshchatx/src/backend/telephone_manager.py @@ -249,7 +249,7 @@ class TelephoneManager: announce = self.db.announces.get_announce_by_hash(canonical) if not announce: # 3) By identity_hash field (if user entered identity hash but we missed recall, or other announce types) - id_key = canonical if canonical else th + id_key = canonical or th announces = self.db.announces.get_filtered_announces( identity_hash=id_key, ) diff --git a/meshchatx/src/backend/voicemail_manager.py b/meshchatx/src/backend/voicemail_manager.py index eb90065..69481fd 100644 --- a/meshchatx/src/backend/voicemail_manager.py +++ b/meshchatx/src/backend/voicemail_manager.py @@ -60,7 +60,7 @@ class VoicemailManager: RNS.log("Voicemail: ffmpeg not found", RNS.LOG_ERROR) def get_name_for_identity_hash(self, identity_hash): - """Default implementation, should be patched by ReticulumMeshChat""" + """Default implementation, should be patched by ReticulumMeshChat.""" return def _find_bundled_binary(self, name): diff --git a/meshchatx/src/path_utils.py b/meshchatx/src/path_utils.py index 59a8c66..c1a63db 100644 --- a/meshchatx/src/path_utils.py +++ b/meshchatx/src/path_utils.py @@ -3,6 +3,7 @@ import os import sys import tempfile + from aiohttp import web diff --git a/meshchatx/src/version.py b/meshchatx/src/version.py index af0e262..899b840 100644 --- a/meshchatx/src/version.py +++ b/meshchatx/src/version.py @@ -1,5 +1,6 @@ -"""Version string synced from package.json. Do not edit by hand. -Run: pnpm run version:sync +"""Version string synced from package.json. + +Do not edit by hand. Run: ``pnpm run version:sync``. """ __version__ = "4.4.0"