BUG-001: web viewer login/session auth (in web viewer commit) BUG-002: db_manager ALTER TABLE for missing channel_operations and feed_message_queue columns on startup BUG-015: scheduler thread blocked on future.result(); replaced all blocking waits with add_done_callback (fire-and-forget) BUG-016: reboot_radio sends meshcore.commands.reboot() before disconnect BUG-017: radio disconnect uses asyncio.wait_for(timeout=10) BUG-022: custom asyncio loop exception handler suppresses IndexError from meshcore parser at DEBUG level BUG-024: last_db_backup_run updated after each run; 2-min startup window; last-run seeded from DB on restart BUG-025: send_channel_message retries up to 2 times (2s delay) on no_event_received via _is_no_event_received() helper BUG-026: split_text_into_chunks() and get_max_message_length() added to CommandManager; keyword dispatch uses send_response_chunked() BUG-028: byte_data = b"" initialised before try block in decode_meshcore_packet to prevent UnboundLocalError in except handler TraceCommand: path nodes reversed and return path truncated; fixed format_elapsed_display: UTC normalisation before elapsed computation (#75) RepeaterManager: auto_manage_contacts guard before any purge logic (#50) Command aliases: [Aliases] config section injects shorthands at startup JSON logging: _JsonFormatter; json_logging = true in [Logging] Structured JSON logging compatible with Loki, Elasticsearch, Splunk Discord bridge, Telegram bridge, and all service plugins updated MeshGraph edge promotion logic corrected Shutdown: scheduler and meshcore disconnect joined cleanly; log spam fixed All modules: ruff and mypy cleanup applied (type annotations, imports)
Alternative Plugins Directory
This directory is for alternative implementations of bot commands that can replace or supplement the default plugins.
Purpose
Some default plugins may not work well in all contexts. For example:
- The
wxcommand uses NOAA data which is primarily for US locations - International users may need weather data from different sources
- Regional variations may require different implementations
Alternative plugins allow you to swap out default plugins without modifying the core codebase.
How It Works
Automatic Replacement by Name
If an alternative plugin has the same name metadata as a default plugin, it will automatically replace the default plugin. For example:
- Default plugin:
modules/commands/wx_command.pywithname = "wx" - Alternative plugin:
modules/commands/alternatives/wx_international.pywithname = "wx"
The alternative plugin will automatically replace the default one.
Configuration-Based Overrides
You can explicitly configure which alternative plugin to use for a command by adding a [Plugin_Overrides] section to your config.ini:
[Plugin_Overrides]
wx = wx_international
This tells the bot to use wx_international.py from the alternatives directory to replace the wx command.
Creating an Alternative Plugin
- Copy the structure: Start with the default plugin as a reference
- Place in alternatives directory: Save your plugin as
modules/commands/alternatives/your_plugin_name.py - Match the plugin name: Set the
nameclass attribute to match the command you want to replace:class WxInternationalCommand(BaseCommand): name = "wx" # This will replace the default wx command keywords = ['wx', 'weather', 'wxa', 'wxalert'] # ... rest of implementation - Implement required methods: Your plugin must inherit from
BaseCommandand implement theexecutemethod - Test: Restart the bot and verify your alternative plugin is loaded
Example: International Weather Plugin
For international users who need weather data from sources other than NOAA:
- Create
modules/commands/alternatives/wx_international.py - Implement a weather command that uses international APIs (e.g., OpenWeatherMap, WeatherAPI.com)
- Set
name = "wx"to replace the default wx command - Optionally add to
config.ini:[Plugin_Overrides] wx = wx_international
Best Practices
- Keep the same interface: Alternative plugins should maintain the same keywords and command interface as the default plugin for consistency
- Document differences: Add comments explaining why this alternative is needed and what makes it different
- Test thoroughly: Make sure your alternative plugin works correctly before deploying
- Version control: Consider keeping alternative plugins in a separate repository or clearly marking them as local modifications
Plugin Loading Order
- Default plugins are loaded first from
modules/commands/ - Configuration-based overrides are applied (from
[Plugin_Overrides]section) - Alternative plugins with matching names automatically replace defaults
- Standalone alternative plugins (with unique names) are loaded as additional commands
Troubleshooting
- Plugin not loading: Check that your file is in
modules/commands/alternatives/and has a.pyextension - Wrong plugin loaded: Verify the
nameattribute matches the command you want to replace - Import errors: Make sure your alternative plugin imports from the correct paths (use relative imports like
from ..base_command import BaseCommand) - Check logs: The bot logs will show which plugins are loaded and from where
Inactive Plugins
The inactive/ subdirectory is for storing alternative plugins you want to keep but not load. Plugins in this directory are completely ignored by the plugin loader.
- Use it to store backup versions, unused alternatives, or plugins you might switch to later
- To activate an inactive plugin, move it from
inactive/back to the mainalternatives/directory - See
inactive/README.mdfor more details
Notes
- Alternative plugins are loaded after default plugins, so they take precedence
- If multiple alternative plugins have the same name, the last one loaded wins
- Configuration-based overrides take precedence over automatic name matching
- The default plugins remain unchanged - alternatives are additive/replacement only
- Plugins in the
inactive/subdirectory are never loaded, even if configured