- Added a tracked `LICENSE` file and updated `pyproject.toml` to include license metadata.
- Enhanced `CHANGELOG.md` with recent changes and clarifications.
- Updated `config.ini.example` and related documentation to reflect clamping behavior for numeric limits in `[Feed_Manager]`.
- Improved startup validation to suggest corrections for unknown and misspelled keys.
- Refactored geocoding and HTTP request handling to run off-thread, preventing event loop stalls.
- Added thread safety to cache management in geocoding functions to avoid race conditions.
Promote the pending 0.9.4 release to 1.0.0 and polish the release
metadata before tagging.
- Bump the version in pyproject.toml and the packaging docs assertion.
- Reframe the CHANGELOG entry as the first stable release, and add the
user-visible changes that shipped without entries: flexible command
prefixes, per-channel flood scope, packet-capture payload decoding,
startup config validation, MOWAS region scoping, hops/RSSI
placeholders, help-command channel overrides, webhook readiness, and
NWS coverage detection.
- Backfill CHANGELOG entries for 0.9.1, 0.9.2, and 0.9.3, which were
tagged but never documented, and add compare links for each.
- Credit external contributors for 0.9.1 and 1.0.0.
- Document the opt-in sender-language detection in the upgrade guide,
alongside the existing optional-extras sections.
- build-deb.sh: fail loudly when the version cannot be read from
pyproject.toml instead of falling back to a hardcoded 0.9.1, which
would stamp a release build with an apparent downgrade.
Reworked implementation of the multilingual-response feature (issue #218)
as a reusable, framework-level capability instead of logic bolted onto
HelloCommand.
- modules/lang_detector.py: keyword-first detection (reliable for the short
greetings typical on mesh) with optional langdetect for longer text;
results are constrained to languages that actually have a translation file.
- core.py: cache Translator instances by language (get_translator) so
per-message switching never re-reads files, and expose available_languages()
derived from translations/*.json. Store translation_path; keep cache/path
consistent on config reload.
- base_command.py: task-local translator override via a ContextVar (safe under
asyncio concurrency, no global mutation), plus detect_response_language() and
a respond_in_sender_language() context manager any command can opt into.
- hello_command.py: build the reply inside respond_in_sender_language(); the
only awaited call (send_response) stays outside the override window.
- Gated by [Localization] auto_detect_language (default false); langdetect is
an optional [lang] extra, not a hard dependency.
Tests: unit coverage for the detector and the base-command wiring.
Three independent wins, measured against a 1.4 GB production database
(714k observed_paths, 38.7k mesh_connections):
- Migration 0014: partial covering index on observed_paths for
bytes_per_hop >= 2. The multibyte evidence query was a full table
scan (2.2 s); with the index it reads only the index (77 ms). The
optional days filter is covered too via last_seen in slot 2.
- Compress responses with flask-compress when the client supports it.
/api/mesh/edges alone is 16.3 MB of JSON uncompressed, 3.9 MB
gzipped. Falls back gracefully (with a warning) if the package is
not installed.
- Fetch stats, edges, and nodes concurrently on the mesh page instead
of three serialized awaits. Node prefixes are now computed client-side
from public_key at the edge prefix length, which removes the
edges-before-nodes ordering dependency. Rendering still waits for
stats so initial framing can use the bot location.
Also call map.invalidateSize() before the initial fitBounds: if the
map was created while its container had no layout (e.g. a background
tab), Leaflet's cached zero width made fitBounds zoom to max.
- Updated the Makefile and GitHub Actions workflow to install ruff version 0.15.15, ensuring consistent linting behavior across environments.
- Added a required-version entry for ruff in pyproject.toml to prevent drift in lint rules.
- Modified the base_service.py method to return None instead of passing, improving clarity.
- Removed an unnecessary blank line in the test_packet_capture_transport_reconnect.py file.
Add a rain nowcast (Open-Meteo 15-minutely precipitation — worldwide, no API
key) as both an on-demand `rain`/`nowcast` command and an opt-in Weather_Service
push that announces rain starting and stopping at the bot's position.
- modules/commands/rain_command.py — command plus pure, unit-tested
fetch/analyze/dedup/label helpers (also reused by the service)
- modules/service_plugins/weather_service.py — background poller mirroring the
existing weather-alert poll pattern; ships disabled (opt-in)
- Location labels resolve to "City, ST" (US) / "City, Country" (non-US)
- 34 unit tests; rain_command added to the strict-mypy module list
- Changed the documentation URL in the meshcore-bot.service file to point to the correct repository.
- Updated the project version in pyproject.toml from 0.9.1 to 0.9.2 for consistency in versioning.
- Modified the User-Agent string in dadjoke_command.py to reflect the new repository URL.
Updated the project version in pyproject.toml to 0.9.1 and adjusted the version retrieval logic in build-deb.sh to reflect this change. This ensures consistency across the project versioning.
- Rewrite test_subscribe_packets/messages_emits_status_ack to match the
silent subscription UX from 1ee84f2.
- Reconcile Python version: requires-python>=3.10, ruff target py310, CI
matrix adds 3.13, pyupgrade UP0xx ignored pending a separate typing-rewrite
PR; fix two B905 zip(strict=...) lints.
- Issue #80 fix in find_recent_rf_data: return None when correlation_key is
provided but unmatched; prefer the longest observed path among samples
sharing a packet_hash; narrow the no-key fallback to a configurable
rf_fallback_window (default 2s).
- Issue #161: lower shipped max_response_hops default 10 -> 7.
- Add CHANGELOG.md, restructure BUGS.md around a ## v0.9.0 Fixed Bugs
table, prune crossed-out duplicate outstanding rows, and add a
Deferred-from-v0.9.0 triage section to TODO.md.
- Untrack coverage.json and add it to .gitignore.
Made-with: Cursor
Added a new PendingMessageEntry TypedDict to improve the structure of pending messages. Updated type hints throughout the MessageHandler class for better clarity and consistency, including the initialization method and message processing functions. This refactor enhances code readability and maintainability without altering existing functionality. Additionally, included the new message_handler module in the pyproject.toml for better module organization.
Introduced optional timeout settings in the configuration for various web viewer operations, including edge and node post timeouts, SQLite connection timeout, and requeue timeout. Updated the web viewer integration to utilize these settings, enhancing flexibility and reliability. Added commands to inspect the resolved configuration with sensitive keys redacted, and updated documentation accordingly.
Bumped the meshcore dependency version from 2.2.31 to 2.3.6 in both pyproject.toml and requirements.txt to ensure compatibility with the latest features and improvements. This change helps maintain the project's dependency integrity.
- Introduced a new `maintenance` module to handle data retention, log rotation, and nightly email tasks.
- Updated the `scheduler` to utilize the `MaintenanceRunner` for executing maintenance tasks, improving code organization and clarity.
- Enhanced documentation to reflect changes in logging configuration and data retention processes.
- Adjusted tests to accommodate the refactored scheduler methods and ensure proper functionality.
- Expanded package data in `pyproject.toml` to include additional static files for the web viewer.
- Removed testing dependencies from `requirements.txt`, noting that they are now included in pyproject extras.
- Updated the base HTML template to reference the manifest from the Flask static folder.
- Modified the build script to exclude `.cursor` files and updated documentation URLs to the correct repository.
- Changed the installation command in the build script to use `requirements.txt` instead of the previous method.
ruff: fix import order, Dict→dict, and unused variable in discord_bridge_service.py
mypy: add types-requests to test deps to resolve import-untyped warnings;
add per-module ignore_errors overrides for modules not yet brought up to
strict typing standard
ShellCheck: remove unused vars (SC2034) in install-service.sh and
uninstall-service.sh; split local declare/assign (SC2155) in
install-service.sh and restart_viewer.sh; replace ls|grep (SC2010) with
glob loops in docker-setup.sh; capture $? immediately after heredoc (SC2320)
Add four jobs to .github/workflows/test.yml:
- lint: ruff check modules/ tests/ — zero violations enforced
- typecheck: mypy modules/ with incremental strict mode; per-module
disallow_untyped_defs where applicable
- lint-frontend: ESLint (eslint-plugin-html) + HTMLHint on templates/
- lint-shell: ShellCheck --severity=warning on all .sh files
Add [tool.ruff] and [tool.mypy] sections to pyproject.toml.
Add .eslintrc.json, .htmlhintrc, package.json for frontend tooling.
- pytest-timeout>=2.1.0 added; timeout=30s per test prevents runaway
tests from hanging CI
- asyncio_mode=auto in pyproject.toml [tool.pytest.ini_options]; async
tests run without per-test markers
- fail_under=27 in [tool.coverage.report] as the enforced coverage
floor; target 40% tracked in TASK-14
- CI test matrix updated for Python 3.9, 3.11, 3.12
- Updated `meshcore` dependency version to `2.2.14` in both `pyproject.toml` and `requirements.txt`.
- Added multi-byte path support in the `PathCommand`, allowing for 1-, 2-, and 3-byte-per-hop paths.
- Enhanced `MessageHandler` to utilize `routing_info` for accurate path extraction and validation.
- Improved path extraction methods in `MultitestCommand` and `TestCommand` to prefer `routing_info` for node IDs.
- Refactored path handling logic across various commands to ensure consistent multi-byte path processing.
- Added new pages to the documentation navigation, including Docker, Upgrade, and Guides sections.
- Updated mkdocs.yml to include the mkdocs-exclude plugin for better content management.
- Modified pyproject.toml to include mkdocs-exclude as a dependency.
- Adjusted GitHub Actions workflow to install the new dependency.
- Removed outdated link from the index.md documentation page.
- Enhanced .gitignore to allow test files in the tests/ directory and committed pytest.ini for test discovery.
- Added checks for missing sections in configuration files, specifically for Admin_ACL and Banned_Users, to prevent errors during bot startup.
- Updated generate_website.py and command_manager.py to handle cases where required sections are absent, returning empty lists instead of raising exceptions.
- Introduced optional dependencies for testing in pyproject.toml, ensuring a smoother development experience.
- Improved localization handling in core.py to default to English when the Localization section is missing, enhancing user experience.
- Deleted COMMANDS.md, DOCKER.md, WEB_VIEWER.md, and other related documentation files to streamline the project structure.
- Updated README.md and other documentation references to point to the new locations of the command and Docker instructions.
- Added optional dependencies for documentation generation in pyproject.toml.
- Refactored bot's main execution flow to use asyncio.run() with improved signal handling for graceful shutdown on Unix systems.
- Added new dependencies in pyproject.toml: urllib3, paho-mqtt, cryptography, and pynacl for enhanced functionality.
- Updated Nix flake to include flake-parts for better modularization and added translation path for NixOS module compatibility.
- Improved argument parsing in web viewer to maintain clarity and consistency.
This commit adds comprehensive Nix/NixOS support and modernizes the
Python packaging structure to enable declarative system-wide deployments.
Added complete Nix flake infrastructure in nix/:
- packages.nix: Package definition with all dependencies
- nixos-module.nix: NixOS module for declarative service configuration
- nixos-test.nix: Comprehensive NixOS VM tests (basic + web viewer)
- shell.nix: Development shell with all build tools
The NixOS module (services.meshcore-bot) provides:
- Automatic user/group creation (meshcore-bot:meshcore-bot)
- Serial port access (dialout group membership)
- Systemd service with security hardening
- Automatic directory management via StateDirectory/LogsDirectory
- INI config generation from Nix attribute sets
- Sensible defaults for system paths
Example NixOS configuration:
services.meshcore-bot = {
enable = true;
settings = {
Connection.connection_type = "serial";
Connection.serial_port = "/dev/ttyUSB0";
Bot.bot_name = "MyBot";
};
};
Migrated from requirements.txt to pyproject.toml:
- Defined proper package metadata and dependencies
- Created entry points: meshcore-bot, meshcore-viewer
- Enables standard 'pip install -e .' workflow
- Maintains compatibility with existing setups
Changes to existing code are minimal and only where necessary:
1. **Added --config parameter** (meshcore_bot.py, app.py)
- Wrapped main logic in main() function for entry point
- Added argparse to accept --config parameter
- Required for NixOS to pass generated config from /nix/store
- Backwards compatible: defaults to 'config.ini' in current directory
- No changes to core bot logic
2. **Fixed web viewer subprocess spawning** (modules/web_viewer/integration.py)
- Changed from sys.executable to 'meshcore-viewer' entry point
- Ensures Nix wrapper script sets up correct PYTHONPATH
- Resolves Flask module import issues in Nix environment
- No functional change for traditional installations
Note: The dev branch's resolve_path() utility already handles both
relative and absolute paths correctly, making it compatible with
NixOS system paths (/var/lib, /var/log) without additional changes.
All NixOS VM tests passing:
- nixos-module-basic: Service startup, file creation, TCP connection
- nixos-module-webviewer: Flask available, port listening, HTTP responses
No breaking changes to existing workflows. The bot continues to work
exactly as before when run with traditional Python methods.