Files
meshcore-bot/modules/commands/alternatives
Stacy Olivas ce884cee87 fix: auth, db migrations, retry, chunking, socket race, trace, timezone, repeater, and ruff/mypy cleanup
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)
2026-03-17 18:07:18 -07:00
..

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 wx command 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.py with name = "wx"
  • Alternative plugin: modules/commands/alternatives/wx_international.py with name = "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

  1. Copy the structure: Start with the default plugin as a reference
  2. Place in alternatives directory: Save your plugin as modules/commands/alternatives/your_plugin_name.py
  3. Match the plugin name: Set the name class 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
    
  4. Implement required methods: Your plugin must inherit from BaseCommand and implement the execute method
  5. 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:

  1. Create modules/commands/alternatives/wx_international.py
  2. Implement a weather command that uses international APIs (e.g., OpenWeatherMap, WeatherAPI.com)
  3. Set name = "wx" to replace the default wx command
  4. Optionally add to config.ini:
    [Plugin_Overrides]
    wx = wx_international
    

Best Practices

  1. Keep the same interface: Alternative plugins should maintain the same keywords and command interface as the default plugin for consistency
  2. Document differences: Add comments explaining why this alternative is needed and what makes it different
  3. Test thoroughly: Make sure your alternative plugin works correctly before deploying
  4. Version control: Consider keeping alternative plugins in a separate repository or clearly marking them as local modifications

Plugin Loading Order

  1. Default plugins are loaded first from modules/commands/
  2. Configuration-based overrides are applied (from [Plugin_Overrides] section)
  3. Alternative plugins with matching names automatically replace defaults
  4. 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 .py extension
  • Wrong plugin loaded: Verify the name attribute 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 main alternatives/ directory
  • See inactive/README.md for 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