diff --git a/generate_website.py b/generate_website.py index 99476e7..b7d7855 100755 --- a/generate_website.py +++ b/generate_website.py @@ -455,7 +455,6 @@ def generate_html(bot_name: str, title: str, introduction: str, commands: List[T for cmd_name, cmd_instance in category_commands: primary_name = cmd_instance.name if hasattr(cmd_instance, 'name') else cmd_name - description = getattr(cmd_instance, 'description', 'No description available') keywords = getattr(cmd_instance, 'keywords', []) # Filter out the primary name from keywords to avoid duplication @@ -464,6 +463,23 @@ def generate_html(bot_name: str, title: str, introduction: str, commands: List[T # Get channel restriction info channel_info = get_channel_info(cmd_instance, monitor_channels) + # Get usage information including usage syntax, examples, parameters, and sub-commands + try: + usage_info = cmd_instance.get_usage_info() + usage_syntax = usage_info.get('usage', '') + examples = usage_info.get('examples', []) + parameters = usage_info.get('parameters', []) + subcommands = usage_info.get('subcommands', []) + # Use short_description for website if available, otherwise fallback to description + short_desc = usage_info.get('short_description', '') + description = short_desc if short_desc else usage_info.get('description', 'No description available') + except Exception: + usage_syntax = '' + examples = [] + parameters = [] + subcommands = [] + description = getattr(cmd_instance, 'description', 'No description available') + commands_html += f'
\n' commands_html += f'
\n' commands_html += f'

{escape_html(primary_name)}

\n' @@ -485,11 +501,28 @@ def generate_html(bot_name: str, title: str, introduction: str, commands: List[T commands_html += f'
\n' commands_html += f'

{escape_html(description)}

\n' - # Get usage information including sub-commands + # Render usage, examples, parameters, subcommands try: - usage_info = cmd_instance.get_usage_info() - subcommands = usage_info.get('subcommands', []) + # Render usage syntax + if usage_syntax: + commands_html += f'
{escape_html(usage_syntax)}
\n' + + # Render parameters + if parameters: + commands_html += f'
\n' + commands_html += f'
Parameters:
\n' + for param in parameters: + param_name = param.get('name', '') + param_desc = param.get('description', '') + if param_name and param_desc: + commands_html += f'
\n' + commands_html += f' {escape_html(param_name)}\n' + commands_html += f' {escape_html(param_desc)}\n' + commands_html += f'
\n' + commands_html += f'
\n' + + # Render subcommands if subcommands: commands_html += f'
\n' commands_html += f'
Sub-commands:
\n' @@ -569,7 +602,7 @@ def generate_html(bot_name: str, title: str, introduction: str, commands: List[T --accent-yellow: #ffd700; --text-primary: #e8edf4; --text-secondary: #8892a4; - --text-muted: #4a5568; + --text-muted: #6b7280; --border-subtle: rgba(255,255,255,0.06); --glow-blue: rgba(0, 212, 255, 0.15); --glow-cyan: rgba(0, 255, 200, 0.1); @@ -1067,6 +1100,57 @@ def generate_html(bot_name: str, title: str, introduction: str, commands: List[T font-size: 0.95rem; }} + .command-usage {{ + margin: 0.75rem 0; + padding: 0.6rem 0.9rem; + background: rgba(0, 0, 0, 0.3); + border-radius: 8px; + border-left: 3px solid var(--accent-blue); + }} + + .command-usage code {{ + font-family: 'JetBrains Mono', monospace; + font-size: 0.85rem; + color: var(--accent-cyan); + }} + + .command-params {{ + margin-top: 0.75rem; + padding-top: 0.75rem; + border-top: 1px solid var(--border-subtle); + }} + + .params-header {{ + font-size: 0.8rem; + font-weight: 600; + color: var(--text-muted); + margin-bottom: 0.5rem; + text-transform: uppercase; + letter-spacing: 0.05em; + }} + + .param-item {{ + display: flex; + gap: 0.5rem; + margin-bottom: 0.4rem; + font-size: 0.85rem; + align-items: flex-start; + }} + + .param-name {{ + font-family: 'JetBrains Mono', monospace; + color: var(--accent-orange); + font-weight: 500; + min-width: 80px; + flex-shrink: 0; + }} + + .param-desc {{ + color: var(--text-secondary); + flex: 1; + line-height: 1.4; + }} + .command-subcommands {{ margin-top: 1rem; padding-top: 1rem; diff --git a/modules/commands/airplanes_command.py b/modules/commands/airplanes_command.py index 5d25c16..8da5f20 100644 --- a/modules/commands/airplanes_command.py +++ b/modules/commands/airplanes_command.py @@ -29,6 +29,16 @@ class AirplanesCommand(BaseCommand): cooldown_seconds = 2 # Respect API rate limit of 1 req/sec with buffer requires_internet = True + # Documentation + short_description = "Get aircraft overhead using ADS-B data" + usage = "airplanes [lat,lon|here] [radius=N] [options]" + examples = ["airplanes", "overhead 47.6,-122.3"] + parameters = [ + {"name": "location", "description": "Coordinates or here for your companion's location if advertised"}, + {"name": "radius", "description": "Search radius in nautical miles (default: 25)"}, + {"name": "filters", "description": "alt=, type=, military, closest, etc."} + ] + def __init__(self, bot): super().__init__(bot) self.airplanes_enabled = self.get_config_value('Airplanes_Command', 'enabled', fallback=True, value_type='bool') diff --git a/modules/commands/alert_command.py b/modules/commands/alert_command.py index f06c6cd..9a29ed1 100644 --- a/modules/commands/alert_command.py +++ b/modules/commands/alert_command.py @@ -178,6 +178,15 @@ class AlertCommand(BaseCommand): description = "Get active emergency incidents (usage: alert seattle, alert 98258, alert 178th seattle, alert seattle all)" category = "emergency" cooldown_seconds = 10 # 10 second cooldown to prevent API abuse + + # Documentation + short_description = "Get active emergency incidents from PulsePoint" + usage = "alert [all]" + examples = ["alert seattle", "alert 98101 all"] + parameters = [ + {"name": "location", "description": "City, zip code, or street address"}, + {"name": "all", "description": "Show all incidents (not just nearby)"} + ] requires_internet = True # Requires internet access for PulsePoint API def __init__(self, bot): diff --git a/modules/commands/alternatives/wx_international.py b/modules/commands/alternatives/wx_international.py index 721e828..026c211 100644 --- a/modules/commands/alternatives/wx_international.py +++ b/modules/commands/alternatives/wx_international.py @@ -25,6 +25,15 @@ class GlobalWxCommand(BaseCommand): cooldown_seconds = 5 # 5 second cooldown per user to prevent API abuse requires_internet = True # Requires internet access for Open-Meteo API and geocoding + # Documentation + short_description = "Get weather for any global location using Open-Meteo API" + usage = "gwx [tomorrow|7d|hourly]" + examples = ["gwx Tokyo", "gwx Paris, France"] + parameters = [ + {"name": "location", "description": "City name, country, or coordinates"}, + {"name": "option", "description": "tomorrow, 7d, or hourly (optional)"} + ] + # Error constants - will use translations instead ERROR_FETCHING_DATA = "ERROR_FETCHING_DATA" # Placeholder, will use translate() NO_ALERTS = "No weather alerts available" diff --git a/modules/commands/aqi_command.py b/modules/commands/aqi_command.py index 922bbdd..c9489cc 100644 --- a/modules/commands/aqi_command.py +++ b/modules/commands/aqi_command.py @@ -31,6 +31,14 @@ class AqiCommand(BaseCommand): cooldown_seconds = 5 # 5 second cooldown per user to prevent API abuse requires_internet = True # Requires internet access for OpenMeteo API and geocoding + # Documentation + short_description = "Get Air Quality Index for a location" + usage = "aqi " + examples = ["aqi seattle", "aqi 47.6,-122.3"] + parameters = [ + {"name": "location", "description": "City, neighborhood, lat/lon, or 'help'"} + ] + # Error constants ERROR_FETCHING_DATA = "Error fetching AQI data" NO_DATA_AVAILABLE = "No AQI data available" diff --git a/modules/commands/base_command.py b/modules/commands/base_command.py index 2ba2d88..8015c69 100644 --- a/modules/commands/base_command.py +++ b/modules/commands/base_command.py @@ -31,6 +31,7 @@ class BaseCommand(ABC): category: str = "general" # Documentation fields - to be overridden by subclasses for website generation + short_description: str = "" # Brief description for website (without usage syntax) usage: str = "" # Usage syntax, e.g., "wx [tomorrow|7d|hourly|alerts]" examples: List[str] = [] # Example commands, e.g., ["wx 98101", "wx seattle tomorrow"] parameters: List[Dict[str, str]] = [] # Parameter definitions, e.g., [{"name": "location", "description": "US zip code or city name"}] @@ -176,21 +177,28 @@ class BaseCommand(ABC): def get_usage_info(self) -> Dict[str, Any]: """Get structured usage information including sub-commands and options. + Uses class attributes as defaults, with translation overrides for i18n support. + Returns: Dict with keys: - - 'description': Main command description + - 'description': Main command description (for help text) + - 'short_description': Brief description for website (without usage syntax) + - 'usage': Usage syntax string (e.g., "wx [option]") - 'subcommands': List of dicts with 'name' and 'description' - - 'usage_patterns': List of usage pattern strings - 'examples': List of example strings + - 'parameters': List of dicts with 'name' and 'description' """ + # Start with class attribute defaults usage_info = { 'description': self.description or "No description available", + 'short_description': self.short_description or "", + 'usage': self.usage or "", 'subcommands': [], - 'usage_patterns': [], - 'examples': [] + 'examples': list(self.examples) if self.examples else [], + 'parameters': list(self.parameters) if self.parameters else [] } - # Try to get structured data from translations + # Try to get structured data from translations (i18n overrides) if hasattr(self.bot, 'translator'): try: # Get subcommands from translations @@ -199,17 +207,23 @@ class BaseCommand(ABC): if subcommands_data and isinstance(subcommands_data, list): usage_info['subcommands'] = subcommands_data - # Get usage patterns from translations - usage_patterns_key = f"commands.{self.name}.usage_patterns" - usage_patterns_data = self.translate_get_value(usage_patterns_key) - if usage_patterns_data and isinstance(usage_patterns_data, list): - usage_info['usage_patterns'] = usage_patterns_data - - # Get examples from translations + # Get examples from translations (override class attribute) examples_key = f"commands.{self.name}.examples" examples_data = self.translate_get_value(examples_key) if examples_data and isinstance(examples_data, list): usage_info['examples'] = examples_data + + # Get usage from translations (override class attribute) + usage_key = f"commands.{self.name}.usage_syntax" + usage_data = self.translate_get_value(usage_key) + if usage_data and isinstance(usage_data, str): + usage_info['usage'] = usage_data + + # Get parameters from translations (override class attribute) + params_key = f"commands.{self.name}.parameters" + params_data = self.translate_get_value(params_key) + if params_data and isinstance(params_data, list): + usage_info['parameters'] = params_data except Exception as e: self.logger.debug(f"Could not load usage info from translations for {self.name}: {e}") diff --git a/modules/commands/channels_command.py b/modules/commands/channels_command.py index 1b69386..b40d32e 100644 --- a/modules/commands/channels_command.py +++ b/modules/commands/channels_command.py @@ -24,6 +24,16 @@ class ChannelsCommand(BaseCommand): description = "Lists hashtag channels with sub-categories. Use 'channels' for general, 'channels list' for all categories, 'channels ' for specific categories, 'channels #channel' for specific channel info." category = "basic" + # Documentation + short_description = "Lists hashtag channels with sub-categories" + usage = "channels [list|category|#channel]" + examples = ["channels", "channels list"] + parameters = [ + {"name": "list", "description": "Show all channel categories"}, + {"name": "category", "description": "Filter by category name"}, + {"name": "#channel", "description": "Get info on a specific channel"} + ] + def __init__(self, bot): """Initialize the channels command. diff --git a/modules/commands/cmd_command.py b/modules/commands/cmd_command.py index 15dfecd..aeb2efa 100644 --- a/modules/commands/cmd_command.py +++ b/modules/commands/cmd_command.py @@ -18,6 +18,11 @@ class CmdCommand(BaseCommand): description = "Lists available commands in compact format" category = "basic" + # Documentation + short_description = "Lists available commands" + usage = "cmd" + examples = ["cmd"] + def __init__(self, bot): """Initialize the cmd command. diff --git a/modules/commands/dadjoke_command.py b/modules/commands/dadjoke_command.py index b84d416..6540266 100644 --- a/modules/commands/dadjoke_command.py +++ b/modules/commands/dadjoke_command.py @@ -24,6 +24,11 @@ class DadJokeCommand(BaseCommand): cooldown_seconds = 3 requires_internet = True # Requires internet access for API calls + # Documentation + short_description = "Get a random dad joke" + usage = "dadjoke" + examples = ["dadjoke"] + # API configuration DAD_JOKE_API_URL = "https://icanhazdadjoke.com/" TIMEOUT = 10 # seconds diff --git a/modules/commands/dice_command.py b/modules/commands/dice_command.py index 666a661..abcfef2 100644 --- a/modules/commands/dice_command.py +++ b/modules/commands/dice_command.py @@ -18,6 +18,20 @@ class DiceCommand(BaseCommand): description = "Roll dice for D&D and tabletop games. Use 'dice' for d6, 'dice d20' for d20, 'dice 2d6' for 2d6, 'dice d10 d6' for mixed dice, 'dice decade' for decade die (00-90), etc." category = "games" + # Documentation + short_description = "Roll dice for tabletop games" + usage = "dice [NdX|dX|decade]" + examples = [ + "dice", + "dice d20", + "dice 2d6", + "dice d10 d6", + "dice decade" + ] + parameters = [ + {"name": "dice", "description": "Dice notation: d6, 2d8, d10 d6, decade"} + ] + # Standard D&D dice types DICE_TYPES = { 'd4': 4, diff --git a/modules/commands/hacker_command.py b/modules/commands/hacker_command.py index 6321ac4..0675533 100644 --- a/modules/commands/hacker_command.py +++ b/modules/commands/hacker_command.py @@ -22,6 +22,11 @@ class HackerCommand(BaseCommand): description = "Simulates hacking a supervillain's mainframe with hilarious error messages" category = "fun" + # Documentation + short_description = "Try Linux commands and get supervillain mainframe errors" + usage = "" + examples = ["sudo make me a sandwich", "rm -rf /"] + def __init__(self, bot: Any): """Initialize the hacker command. diff --git a/modules/commands/hello_command.py b/modules/commands/hello_command.py index e3ce22c..02c09dd 100644 --- a/modules/commands/hello_command.py +++ b/modules/commands/hello_command.py @@ -19,6 +19,11 @@ class HelloCommand(BaseCommand): description = "Responds to greetings with robot-themed responses" category = "basic" + # Documentation + short_description = "Responds to greetings with robot-themed responses" + usage = "hello" + examples = ["hello", "hi", "hey"] + def __init__(self, bot: Any): """Initialize the hello command. diff --git a/modules/commands/help_command.py b/modules/commands/help_command.py index db2a30f..52d4deb 100644 --- a/modules/commands/help_command.py +++ b/modules/commands/help_command.py @@ -25,6 +25,14 @@ class HelpCommand(BaseCommand): description = "Shows commands. Use 'help ' for details." category = "basic" + # Documentation + short_description = "Get help on available commands" + usage = "help [command]" + examples = ["help", "help wx"] + parameters = [ + {"name": "command", "description": "Command name for detailed help (optional)"} + ] + def __init__(self, bot): """Initialize the help command. diff --git a/modules/commands/hfcond_command.py b/modules/commands/hfcond_command.py index 94d1afc..6b96cc5 100644 --- a/modules/commands/hfcond_command.py +++ b/modules/commands/hfcond_command.py @@ -22,6 +22,11 @@ class HfcondCommand(BaseCommand): category = "solar" requires_internet = True # Requires internet access for hamqsl.com API + # Documentation + short_description = "Get HF band conditions for ham radio" + usage = "hfcond" + examples = ["hfcond"] + def __init__(self, bot): """Initialize the hfcond command. diff --git a/modules/commands/joke_command.py b/modules/commands/joke_command.py index 9755dfd..552d7fd 100644 --- a/modules/commands/joke_command.py +++ b/modules/commands/joke_command.py @@ -24,6 +24,14 @@ class JokeCommand(BaseCommand): requires_dm = False # Works in both channels and DMs requires_internet = True # Requires internet access for API calls + # Documentation + short_description = "Get a random joke" + usage = "joke [category]" + examples = ["joke", "joke programming"] + parameters = [ + {"name": "category", "description": "programming, pun, misc, dark (optional)"} + ] + # Supported categories SUPPORTED_CATEGORIES = { 'programming': 'Programming', diff --git a/modules/commands/magic8_command.py b/modules/commands/magic8_command.py index 97a884f..5b1b16a 100644 --- a/modules/commands/magic8_command.py +++ b/modules/commands/magic8_command.py @@ -27,6 +27,11 @@ class Magic8Command(BaseCommand): description = "Emulates the classic Magic 8-ball toy'" category = "games" + # Documentation + short_description = "Ask the Magic 8-Ball a yes/no question" + usage = "magic8 " + examples = ["magic8 Will it rain tomorrow?"] + def __init__(self, bot): """Initialize the magic8 command. diff --git a/modules/commands/moon_command.py b/modules/commands/moon_command.py index 6498c47..feb61e3 100644 --- a/modules/commands/moon_command.py +++ b/modules/commands/moon_command.py @@ -17,6 +17,11 @@ class MoonCommand(BaseCommand): description = "Get moon phase, rise/set times and position" category = "solar" + # Documentation + short_description = "Get moon phase and rise/set times" + usage = "moon" + examples = ["moon"] + def __init__(self, bot): """Initialize the moon command. diff --git a/modules/commands/multitest_command.py b/modules/commands/multitest_command.py index 543e829..09731bd 100644 --- a/modules/commands/multitest_command.py +++ b/modules/commands/multitest_command.py @@ -21,6 +21,11 @@ class MultitestCommand(BaseCommand): description = "Listens for 6 seconds and collects all unique paths from incoming messages" category = "meshcore_info" + # Documentation + short_description = "Listens for 6 seconds and collects all unique paths your incoming messages took to reach the bot" + usage = "multitest" + examples = ["multitest", "mt"] + def __init__(self, bot): super().__init__(bot) self.multitest_enabled = self.get_config_value('Multitest_Command', 'enabled', fallback=True, value_type='bool') diff --git a/modules/commands/path_command.py b/modules/commands/path_command.py index 5765a4f..e3cd6c2 100644 --- a/modules/commands/path_command.py +++ b/modules/commands/path_command.py @@ -24,6 +24,11 @@ class PathCommand(BaseCommand): cooldown_seconds = 1 category = "meshcore_info" + # Documentation + short_description = "Decode path data to show repeaters involved in message routing" + usage = "path [hex_data]" + examples = ["path", "decode"] + def __init__(self, bot): super().__init__(bot) self.path_enabled = self.get_config_value('Path_Command', 'enabled', fallback=True, value_type='bool') diff --git a/modules/commands/ping_command.py b/modules/commands/ping_command.py index 7b0e11f..8024955 100644 --- a/modules/commands/ping_command.py +++ b/modules/commands/ping_command.py @@ -22,6 +22,11 @@ class PingCommand(BaseCommand): description = "Responds to 'ping' with 'Pong!'" category = "basic" + # Documentation + short_description = "Get a quick 'pong'response from the bot" + usage = "ping" + examples = ["ping"] + def __init__(self, bot): """Initialize the ping command. diff --git a/modules/commands/prefix_command.py b/modules/commands/prefix_command.py index 907dc12..b948e97 100644 --- a/modules/commands/prefix_command.py +++ b/modules/commands/prefix_command.py @@ -27,6 +27,15 @@ class PrefixCommand(BaseCommand): cooldown_seconds = 2 requires_internet = False # Will be set to True in __init__ if API is configured + # Documentation + short_description = "Look up repeaters by two-character prefix and show their locations (if known)" + usage = "prefix " + examples = ["prefix 1A", "prefix free"] + parameters = [ + {"name": "prefix", "description": "Two-character prefix (e.g., 1A, 2B)"}, + {"name": "free", "description": "Show available/unused prefixes"} + ] + def __init__(self, bot: Any): """Initialize the prefix command. diff --git a/modules/commands/roll_command.py b/modules/commands/roll_command.py index 7c98f74..ed692b7 100644 --- a/modules/commands/roll_command.py +++ b/modules/commands/roll_command.py @@ -24,6 +24,14 @@ class RollCommand(BaseCommand): description = "Roll a random number between 1 and X (default 100). Use 'roll' for 1-100, 'roll 50' for 1-50, etc." category = "games" + # Documentation + short_description = "Roll a random number between 1 and X" + usage = "roll [max]" + examples = ["roll", "roll 50"] + parameters = [ + {"name": "max", "description": "Maximum value (default: 100, max: 10000)"} + ] + def __init__(self, bot): """Initialize the roll command. diff --git a/modules/commands/satpass_command.py b/modules/commands/satpass_command.py index 1a4c202..247cf6b 100644 --- a/modules/commands/satpass_command.py +++ b/modules/commands/satpass_command.py @@ -18,6 +18,15 @@ class SatpassCommand(BaseCommand): category = "solar" requires_internet = True # Requires internet access for N2YO API + # Documentation + short_description = "Get satellite pass predictions" + usage = "satpass [visual]" + examples = ["satpass iss", "satpass 25544 visual"] + parameters = [ + {"name": "satellite", "description": "NORAD ID or shortcut (iss, hst, starlink)"}, + {"name": "visual", "description": "Add 'visual' for visible passes only"} + ] + # Common satellite shortcuts SATELLITE_SHORTCUTS = { 'iss': '25544', diff --git a/modules/commands/solar_command.py b/modules/commands/solar_command.py index 598db75..6c3cbbe 100644 --- a/modules/commands/solar_command.py +++ b/modules/commands/solar_command.py @@ -22,6 +22,11 @@ class SolarCommand(BaseCommand): category = "solar" requires_internet = True # Requires internet access for hamqsl.com API + # Documentation + short_description = "Get current solar conditions and HF band info" + usage = "solar" + examples = ["solar"] + def __init__(self, bot): """Initialize the solar command. diff --git a/modules/commands/solarforecast_command.py b/modules/commands/solarforecast_command.py index dae12ef..5691ae4 100644 --- a/modules/commands/solarforecast_command.py +++ b/modules/commands/solarforecast_command.py @@ -29,6 +29,17 @@ class SolarforecastCommand(BaseCommand): cooldown_seconds = 10 # 10 second cooldown per user requires_internet = True # Requires internet access for Forecast.Solar API and geocoding + # Documentation + short_description = "Get solar panel production forecast for a location or repeater" + usage = "sf [watts] [azimuth] [angle]" + examples = ["sf seattle", "sf 47.6,-122.3 200"] + parameters = [ + {"name": "location", "description": "City, coordinates, or repeater name"}, + {"name": "watts", "description": "Panel size in watts (default: 100)"}, + {"name": "azimuth", "description": "Panel direction, 0=south (default: 0)"}, + {"name": "angle", "description": "Panel tilt in degrees (default: 30)"} + ] + # Error constants - will use translations instead ERROR_FETCHING_DATA = "Error fetching forecast" # Deprecated - use translate NO_DATA_AVAILABLE = "No forecast data" # Deprecated - use translate diff --git a/modules/commands/sports_command.py b/modules/commands/sports_command.py index d93fe7b..65d530c 100644 --- a/modules/commands/sports_command.py +++ b/modules/commands/sports_command.py @@ -44,6 +44,15 @@ class SportsCommand(BaseCommand): cooldown_seconds = 3 # 3 second cooldown per user to prevent API abuse requires_internet = True # Requires internet access for ESPN API + # Documentation + short_description = "Get sports scores and schedules" + usage = "sports [team|league]" + examples = ["sports", "sports seahawks", "sports nfl"] + parameters = [ + {"name": "team", "description": "Team name (e.g., seahawks, mariners)"}, + {"name": "league", "description": "League code (nfl, mlb, nba, nhl, mls)"} + ] + # ESPN client espn_client: Optional[ESPNClient] = None diff --git a/modules/commands/stats_command.py b/modules/commands/stats_command.py index 12be104..a6d97a7 100644 --- a/modules/commands/stats_command.py +++ b/modules/commands/stats_command.py @@ -25,6 +25,14 @@ class StatsCommand(BaseCommand): description = "Show statistics for past 24 hours. Use 'stats messages', 'stats channels', or 'stats paths' for specific stats." category = "analytics" + # Documentation + short_description = "Show bot usage statistics for past 24 hours" + usage = "stats [messages|channels|paths]" + examples = ["stats", "stats channels"] + parameters = [ + {"name": "type", "description": "messages, channels, or paths (optional)"} + ] + def __init__(self, bot: Any): """Initialize the stats command. diff --git a/modules/commands/sun_command.py b/modules/commands/sun_command.py index 07ffbd4..f48cb73 100644 --- a/modules/commands/sun_command.py +++ b/modules/commands/sun_command.py @@ -21,6 +21,11 @@ class SunCommand(BaseCommand): description = "Get sunrise/sunset times" category = "solar" + # Documentation + short_description = "Get sunrise and sunset times" + usage = "sun" + examples = ["sun"] + def __init__(self, bot): """Initialize the sun command. diff --git a/modules/commands/test_command.py b/modules/commands/test_command.py index 5ca7f1c..c763f9a 100644 --- a/modules/commands/test_command.py +++ b/modules/commands/test_command.py @@ -26,6 +26,11 @@ class TestCommand(BaseCommand): description = "Responds to 'test' or 't' with connection info" category = "basic" + # Documentation + short_description = "Get test response with connection info" + usage = "test [phrase]" + examples = ["test", "t hello world"] + def __init__(self, bot): super().__init__(bot) self.test_enabled = self.get_config_value('Test_Command', 'enabled', fallback=True, value_type='bool') diff --git a/modules/commands/wx_command.py b/modules/commands/wx_command.py index 1b12520..eb59668 100644 --- a/modules/commands/wx_command.py +++ b/modules/commands/wx_command.py @@ -38,6 +38,15 @@ class WxCommand(BaseCommand): cooldown_seconds = 5 # 5 second cooldown per user to prevent API abuse requires_internet = True # Requires internet access for NOAA API and geocoding + # Documentation + short_description = "Get weather for a US location using NOAA weather data" + usage = "wx [tomorrow|7d|hourly|alerts]" + examples = ["wx 98101", "wx seattle", "wx 90210 7d"] + parameters = [ + {"name": "location", "description": "US zip code or city name"}, + {"name": "option", "description": "tomorrow, 7d, hourly, or alerts (optional)"} + ] + # Error constants NO_DATA_NOGPS = "No GPS data available" ERROR_FETCHING_DATA = "Error fetching weather data" diff --git a/website/index.html b/website/index.html index 7356ecf..7fdcea5 100644 --- a/website/index.html +++ b/website/index.html @@ -21,7 +21,7 @@ --accent-yellow: #ffd700; --text-primary: #e8edf4; --text-secondary: #8892a4; - --text-muted: #4a5568; + --text-muted: #6b7280; --border-subtle: rgba(255,255,255,0.06); --glow-blue: rgba(0, 212, 255, 0.15); --glow-cyan: rgba(0, 255, 200, 0.1); @@ -519,6 +519,57 @@ font-size: 0.95rem; } + .command-usage { + margin: 0.75rem 0; + padding: 0.6rem 0.9rem; + background: rgba(0, 0, 0, 0.3); + border-radius: 8px; + border-left: 3px solid var(--accent-blue); + } + + .command-usage code { + font-family: 'JetBrains Mono', monospace; + font-size: 0.85rem; + color: var(--accent-cyan); + } + + .command-params { + margin-top: 0.75rem; + padding-top: 0.75rem; + border-top: 1px solid var(--border-subtle); + } + + .params-header { + font-size: 0.8rem; + font-weight: 600; + color: var(--text-muted); + margin-bottom: 0.5rem; + text-transform: uppercase; + letter-spacing: 0.05em; + } + + .param-item { + display: flex; + gap: 0.5rem; + margin-bottom: 0.4rem; + font-size: 0.85rem; + align-items: flex-start; + } + + .param-name { + font-family: 'JetBrains Mono', monospace; + color: var(--accent-orange); + font-weight: 500; + min-width: 80px; + flex-shrink: 0; + } + + .param-desc { + color: var(--text-secondary); + flex: 1; + line-height: 1.4; + } + .command-subcommands { margin-top: 1rem; padding-top: 1rem; @@ -884,7 +935,8 @@ t
-

Responds to 'test' or 't' with connection info

+

Get test response with connection info

+
test [phrase]
@@ -899,18 +951,28 @@

Responds to greetings with robot-themed responses

+
hello

ping

-

Responds to 'ping' with 'Pong!'

+

Get a quick 'pong'response from the bot

+
ping

help

-

Shows commands. Use 'help <command>' for details.

+

Get help on available commands

+
help [command]
+
+
Parameters:
+
+ command + Command name for detailed help (optional) +
+
@@ -919,7 +981,23 @@ channel
-

Lists hashtag channels with sub-categories. Use 'channels' for general, 'channels list' for all categories, 'channels <category>' for specific categories, 'channels #channel' for specific channel info.

+

Lists hashtag channels with sub-categories

+
channels [list|category|#channel]
+
+
Parameters:
+
+ list + Show all channel categories +
+
+ category + Filter by category name +
+
+ #channel + Get info on a specific channel +
+
@@ -928,7 +1006,8 @@ commands
-

Lists available commands in compact format

+

Lists available commands

+
cmd
@@ -939,7 +1018,15 @@

stats

-

Show statistics for past 24 hours. Use 'stats messages', 'stats channels', or 'stats paths' for specific stats.

+

Show bot usage statistics for past 24 hours

+
stats [messages|channels|paths]
+
+
Parameters:
+
+ type + messages, channels, or paths (optional) +
+
@@ -955,7 +1042,19 @@ incidents -

Get active emergency incidents (usage: alert seattle, alert 98258, alert 178th seattle, alert seattle all)

+

Get active emergency incidents from PulsePoint

+
alert <location> [all]
+
+
Parameters:
+
+ location + City, zip code, or street address +
+
+ all + Show all incidents (not just nearby) +
+
@@ -969,7 +1068,15 @@ jokes -

Get a random joke or joke from specific category (usage: joke [category])

+

Get a random joke

+
joke [category]
+
+
Parameters:
+
+ category + programming, pun, misc, dark (optional) +
+
Channel: #jokes
@@ -986,7 +1093,8 @@ dad jokes -

Get a random dad joke from icanhazdadjoke.com

+

Get a random dad joke

+
dadjoke
Channel: #jokes
@@ -1001,7 +1109,8 @@ +24 more
-

Simulates hacking a supervillain's mainframe with hilarious error messages

+

Try Linux commands and get supervillain mainframe errors

+
<linux_command>
@@ -1012,19 +1121,36 @@

dice

-

Roll dice for D&D and tabletop games. Use 'dice' for d6, 'dice d20' for d20, 'dice 2d6' for 2d6, 'dice d10 d6' for mixed dice, 'dice decade' for decade die (00-90), etc.

+

Roll dice for tabletop games

+
dice [NdX|dX|decade]
+
+
Parameters:
+
+ dice + Dice notation: d6, 2d8, d10 d6, decade +
+

magic8

-

Emulates the classic Magic 8-ball toy'

+

Ask the Magic 8-Ball a yes/no question

+
magic8 <question>

roll

-

Roll a random number between 1 and X (default 100). Use 'roll' for 1-100, 'roll 50' for 1-50, etc.

+

Roll a random number between 1 and X

+
roll [max]
+
+
Parameters:
+
+ max + Maximum value (default: 100, max: 10000) +
+
@@ -1041,7 +1167,23 @@ overhead -

Get aircraft overhead (usage: airplanes [location] [options] or overhead [lat,lon])

+

Get aircraft overhead using ADS-B data

+
airplanes [lat,lon|here] [radius=N] [options]
+
+
Parameters:
+
+ location + Coordinates or here for your companion's location if advertised +
+
+ radius + Search radius in nautical miles (default: 25) +
+
+ filters + alt=, type=, military, closest, etc. +
+
@@ -1057,7 +1199,8 @@ p -

Decode hex path data to show which repeaters were involved in message routing

+

Decode path data to show repeaters involved in message routing

+
path [hex_data]
@@ -1067,7 +1210,19 @@ lookup
-

Look up repeaters by two-character prefix (e.g., 'prefix 1A')

+

Look up repeaters by two-character prefix and show their locations (if known)

+
prefix <XX|free|refresh>
+
+
Parameters:
+
+ prefix + Two-character prefix (e.g., 1A, 2B) +
+
+ free + Show available/unused prefixes +
+
@@ -1076,7 +1231,8 @@ mt
-

Listens for 6 seconds and collects all unique paths from incoming messages

+

Listens for 6 seconds and collects all unique paths your incoming messages took to reach the bot

+
multitest
@@ -1088,30 +1244,46 @@

hfcond

Get HF band conditions for ham radio

+
hfcond

moon

-

Get moon phase, rise/set times and position

+

Get moon phase and rise/set times

+
moon

solar

Get current solar conditions and HF band info

+
solar

satpass

-

Get satellite pass info: satpass <NORAD_number_or_shortcut> [visual]

+

Get satellite pass predictions

+
satpass <NORAD_number|shortcut> [visual]
+
+
Parameters:
+
+ satellite + NORAD ID or shortcut (iss, hst, starlink) +
+
+ visual + Add 'visual' for visible passes only +
+

sun

-

Get sunrise/sunset times

+

Get sunrise and sunset times

+
sun
@@ -1120,7 +1292,27 @@ sf
-

Get solar panel production forecast (usage: sf <location|repeater_name|coordinates|zipcode> [panel_size] [azimuth, 0=south] [angle])

+

Get solar panel production forecast for a location or repeater

+
sf <location> [watts] [azimuth] [angle]
+
+
Parameters:
+
+ location + City, coordinates, or repeater name +
+
+ watts + Panel size in watts (default: 100) +
+
+ azimuth + Panel direction, 0=south (default: 0) +
+
+ angle + Panel tilt in degrees (default: 30) +
+
@@ -1135,7 +1327,19 @@ scores -

Get sports scores and schedules (usage: sports [team/league])

+

Get sports scores and schedules

+
sports [team|league]
+
+
Parameters:
+
+ team + Team name (e.g., seahawks, mariners) +
+
+ league + League code (nfl, mlb, nba, nhl, mls) +
+
Channels: #BotTest, #bot, #sounders, #seahawks
@@ -1152,7 +1356,19 @@ wxalert -

Get weather information for a zip code (usage: wx 12345)

+

Get weather for a US location using NOAA weather data

+
wx <zipcode|city> [tomorrow|7d|hourly|alerts]
+
+
Parameters:
+
+ location + US zip code or city name +
+
+ option + tomorrow, 7d, hourly, or alerts (optional) +
+
@@ -1162,7 +1378,19 @@ gwxa
-

Get weather information for any global location (usage: gwx Tokyo)

+

Get weather for any global location using Open-Meteo API

+
gwx <location> [tomorrow|7d|hourly]
+
+
Parameters:
+
+ location + City name, country, or coordinates +
+
+ option + tomorrow, 7d, or hourly (optional) +
+
@@ -1173,7 +1401,15 @@ air_quality
-

Get Air Quality Index for a location (usage: aqi seattle, aqi greenwood, aqi vancouver canada, aqi 47.6,-122.3, or aqi help)

+

Get Air Quality Index for a location

+
aqi <city|neighborhood|coordinates|help>
+
+
Parameters:
+
+ location + City, neighborhood, lat/lon, or 'help' +
+