mirror of
https://github.com/agessaman/meshcore-bot.git
synced 2026-10-23 14:22:22 +00:00
refactor(commands): every bundled get_help_text takes message=None
S4. BaseCommand and the 40 bundled overrides that took no argument now take `message: MeshMessage | None = None`, matching joke and rain, which already use the message. None of the 40 bodies read it, so their help text is unchanged. help and command_manager keep their TypeError retry, since local plugins may still implement get_help_text(self). For bundled commands the retry no longer runs: `help <command>` calls get_help_text(message) once instead of failing and calling it again. A new test pins the signature for every bundled command class. The offline smoke run against the real config produces the same help text for all 46 commands.
This commit is contained in:
1 parent
596a369204
commit
7aba432b54
41 files changed
+75
-40
No files matched your search
@@ -37,7 +37,7 @@ class AdvertCommand(BaseCommand):
|
||||
super().__init__(bot)
|
||||
self.advert_enabled = self.get_config_value('Advert_Command', 'enabled', fallback=True, value_type='bool')
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
"""Get help text for the advert command.
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -103,7 +103,7 @@ class AirplanesCommand(BaseCommand):
|
||||
self.max_results = self.get_config_value('Airplanes_Command', 'max_results', fallback=3, value_type='int')
|
||||
self.url_timeout = self.get_config_value('Airplanes_Command', 'url_timeout', fallback=10, value_type='int')
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
"""Get help text for this command.
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -141,7 +141,7 @@ class GlobalWxCommand(WeatherCommandMixin, BaseCommand):
|
||||
"""
|
||||
return load_open_meteo_model(self.bot.config, self.logger)
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
"""Get help text for the command.
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -135,7 +135,7 @@ class AqiCommand(BaseCommand):
|
||||
'comet': "Comets have thin atmospheres of water vapor and dust. AQI: Variable, but you'd freeze in space anyway. ☄️"
|
||||
}
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
region = self.default_state or self.default_country
|
||||
return f"Usage: aqi <city|neighborhood|city country|lat,lon|help> - Get AQI for city/neighborhood in {region}, intl cities, coordinates, or help"
|
||||
|
||||
|
||||
@@ -376,7 +376,7 @@ class BaseCommand(ABC):
|
||||
return False
|
||||
return True
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
"""Get help text for this command.
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -117,7 +117,7 @@ class CatfactCommand(BaseCommand):
|
||||
"""
|
||||
return self.translated_or('commands.catfact.facts', self.cat_facts_fallback)
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
"""Get help text for the catfact command.
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -23,7 +23,7 @@ class ChannelPauseCommand(BaseCommand):
|
||||
def __init__(self, bot):
|
||||
super().__init__(bot)
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
return (
|
||||
"Controls whether the bot responds to public channel messages.\n"
|
||||
"DMs always work (including this command).\n"
|
||||
|
||||
@@ -63,7 +63,7 @@ class ChannelsCommand(BaseCommand):
|
||||
super().__init__(bot)
|
||||
self.channels_enabled = self.get_config_value('Channels_Command', 'enabled', fallback=True, value_type='bool')
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
return self.translate('commands.channels.help')
|
||||
|
||||
def matches_keyword(self, message: MeshMessage) -> bool:
|
||||
|
||||
@@ -41,7 +41,7 @@ class CmdCommand(BaseCommand):
|
||||
'Cmd_Command', 'cmd_reference_url', fallback='', value_type='str'
|
||||
).strip()
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
"""Get help text for the cmd command.
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -39,7 +39,7 @@ class ContactCommand(BaseCommand):
|
||||
super().__init__(bot)
|
||||
self.enabled = self.get_config_value('Contact_Command', 'enabled', fallback=True, value_type='bool')
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
"""Get help text for the contact command.
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -66,7 +66,7 @@ class DadJokeCommand(BaseCommand):
|
||||
self.dadjoke_enabled = self.get_config_value('DadJoke_Command', 'dadjoke_enabled', fallback=True, value_type='bool')
|
||||
self.long_jokes = self.get_config_value('DadJoke_Command', 'long_jokes', fallback=False, value_type='bool')
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
"""Get help text for the dadjoke command.
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -57,7 +57,7 @@ class DiceCommand(BaseCommand):
|
||||
super().__init__(bot)
|
||||
self.dice_enabled = self.get_config_value('Dice_Command', 'enabled', fallback=True, value_type='bool')
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
"""Get help text for the dice command.
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -83,7 +83,7 @@ class FeedCommand(BaseCommand):
|
||||
else:
|
||||
return await self.send_response(message, self.get_help_text())
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
"""Get help text for feed command"""
|
||||
return """Feed Command Usage:
|
||||
feed subscribe <rss|api> <url> <channel> [name]
|
||||
|
||||
@@ -1514,7 +1514,7 @@ class GreeterCommand(BaseCommand):
|
||||
# Mark as greeted so we don't greet them later
|
||||
self.mark_as_greeted(key[0], key[1])
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
"""Get help text for the greeter command.
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -429,7 +429,7 @@ class HackerCommand(BaseCommand):
|
||||
if self.enabled is None:
|
||||
self.enabled = self.get_config_value('Hacker_Command', 'hacker_enabled', fallback=False, value_type='bool')
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
"""Get help text for the hacker command.
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -285,7 +285,7 @@ class HelloCommand(BaseCommand):
|
||||
"""
|
||||
return self.translated_or('commands.hello.emoji_responses', self.emoji_responses_fallback, dict)
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
"""Get help text for the hello command.
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -47,7 +47,7 @@ class HelpCommand(BaseCommand):
|
||||
super().__init__(bot)
|
||||
self.help_enabled = self.get_config_value('Help_Command', 'enabled', fallback=True, value_type='bool')
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
"""Get help text for the help command.
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -61,7 +61,7 @@ class HfcondCommand(BaseCommand):
|
||||
error_msg = self.translate('commands.hfcond.error', error=str(e))
|
||||
return await self.send_response(message, error_msg)
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
"""Get help text for this command.
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -46,7 +46,7 @@ class Magic8Command(BaseCommand):
|
||||
super().__init__(bot)
|
||||
self.magic8_enabled = self.get_config_value('Magic8_Command', 'enabled', fallback=True, value_type='bool')
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
"""Get help text for the magic8 command.
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -166,7 +166,7 @@ class MoonCommand(BaseCommand):
|
||||
# Fallback to original format if formatting fails
|
||||
return self.translate('commands.moon.fallback', info=moon_info)
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
"""Get help text for this command.
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -530,7 +530,7 @@ class MultitestCommand(BaseCommand):
|
||||
"""True when any condensed layout is enabled (flat or nested)."""
|
||||
return self.condense_paths_mode != "off"
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
return self.translate('commands.multitest.help', fallback="Listens for 6 seconds and collects all unique paths from incoming messages")
|
||||
|
||||
def extract_path_from_rf_data(self, rf_data: dict) -> Optional[str]:
|
||||
|
||||
@@ -60,7 +60,7 @@ class NeighborsCommand(BaseCommand):
|
||||
# where the scheduler's own trigger is also visible.
|
||||
self._cycle_task: Optional[asyncio.Task] = None
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
"""Get help text for the neighbors command.
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -1097,6 +1097,6 @@ class PathCommand(BaseCommand):
|
||||
"""Get help text for the path command"""
|
||||
return self.translate('commands.path.help')
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
"""Get help text for the path command (used by help system)"""
|
||||
return self.get_help()
|
||||
@@ -39,7 +39,7 @@ class PingCommand(BaseCommand):
|
||||
super().__init__(bot)
|
||||
self.ping_enabled = self.get_config_value('Ping_Command', 'enabled', fallback=True, value_type='bool')
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
"""Get help text for the ping command.
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -157,7 +157,7 @@ class PrefixCommand(BaseCommand):
|
||||
self.prefix_best_location_radius_km = 50.0
|
||||
self.prefix_best_do_not_suggest = []
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
"""Get help text for the prefix command.
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -29,7 +29,7 @@ class ReloadCommand(BaseCommand):
|
||||
"""
|
||||
super().__init__(bot)
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
"""Get help text for the reload command.
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -45,7 +45,7 @@ class RollCommand(BaseCommand):
|
||||
super().__init__(bot)
|
||||
self.roll_enabled = self.get_config_value('Roll_Command', 'enabled', fallback=True, value_type='bool')
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
"""Get help text for the roll command.
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -103,7 +103,7 @@ class SatpassCommand(BaseCommand):
|
||||
await self.send_response(message, error_msg)
|
||||
return False
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
"""Get help text for this command.
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -56,7 +56,7 @@ class ScheduleCommand(BaseCommand):
|
||||
return False
|
||||
return super().can_execute(message)
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
return "schedule [list] — show scheduled messages and advert interval"
|
||||
|
||||
async def execute(self, message: MeshMessage) -> bool:
|
||||
|
||||
@@ -67,7 +67,7 @@ class SolarCommand(BaseCommand):
|
||||
await self.send_response(message, error_msg)
|
||||
return False
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
"""Get help text for this command.
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -106,7 +106,7 @@ class SolarforecastCommand(BaseCommand):
|
||||
# Get database manager for geocoding cache
|
||||
self.db_manager = bot.db_manager
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
return self.translate('commands.solarforecast.usage')
|
||||
|
||||
def _translate_day_abbreviation(self, day_abbr: str) -> str:
|
||||
|
||||
@@ -141,7 +141,7 @@ class SportsCommand(BaseCommand):
|
||||
|
||||
return self._cleaned_content_matches(message, _matches)
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
return self.translate('commands.sports.help')
|
||||
|
||||
|
||||
|
||||
@@ -342,7 +342,7 @@ class StatsCommand(BaseCommand):
|
||||
# If it's already a single node ID or short path, return as-is
|
||||
return path
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
"""Get help text for the stats command.
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -62,7 +62,7 @@ class SunCommand(BaseCommand):
|
||||
error_msg = self.translate('commands.sun.error', error=str(e))
|
||||
return await self.send_response(message, error_msg)
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
"""Get help text for this command.
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -86,7 +86,7 @@ class TestCommand(BaseCommand):
|
||||
except Exception as e:
|
||||
self.logger.warning(f"Error reading bot location from config: {e}")
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
"""Get help text for the command.
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -82,7 +82,7 @@ class TraceCommand(BaseCommand):
|
||||
output_fmt = (self.bot.config.get("Trace_Command", "output_format", fallback="inline") or "inline").strip().lower()
|
||||
self.output_format = output_fmt if output_fmt in ("inline", "vertical") else "inline"
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
return (
|
||||
"trace [path] — run trace along path (return may not be heard). No path = round-trip like tracer. "
|
||||
"tracer [path] — round-trip so bot hears return. Path: comma-separated hex nodes "
|
||||
|
||||
@@ -37,7 +37,7 @@ class VersionCommand(BaseCommand):
|
||||
value_type="bool",
|
||||
)
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
return self.description
|
||||
|
||||
async def execute(self, message: MeshMessage) -> bool:
|
||||
|
||||
@@ -30,7 +30,7 @@ class WebViewerCommand(BaseCommand):
|
||||
super().__init__(bot)
|
||||
self.webviewer_enabled = self.get_config_value('WebViewer_Command', 'enabled', fallback=True, value_type='bool')
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
"""Get help text for the webviewer command.
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -81,7 +81,7 @@ class WorldCupCommand(BaseCommand):
|
||||
|
||||
return self._cleaned_content_matches(message, _matches)
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
return self.translate("commands.worldcup.help")
|
||||
|
||||
# ------------------------------------------------------------------ helpers
|
||||
|
||||
@@ -366,7 +366,7 @@ class WxCommand(WeatherCommandMixin, BaseCommand):
|
||||
"""Move blocking provider work off-loop without concurrent Session use."""
|
||||
return await asyncio.to_thread(self._run_sync_provider, operation, *args, **kwargs)
|
||||
|
||||
def get_help_text(self) -> str:
|
||||
def get_help_text(self, message: MeshMessage | None = None) -> str:
|
||||
"""Get help text, delegating to international command if using Open-Meteo"""
|
||||
if self.delegate_command:
|
||||
return self.delegate_command.get_help_text()
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
"""Every bundled command takes ``get_help_text(message=None)``.
|
||||
|
||||
help and command_manager still retry without the message when a command's
|
||||
get_help_text takes none (local plugins may be written that way), but bundled
|
||||
commands use the one signature.
|
||||
"""
|
||||
|
||||
import importlib
|
||||
import inspect
|
||||
import pkgutil
|
||||
|
||||
import modules.commands
|
||||
import modules.commands.alternatives
|
||||
from modules.commands.base_command import BaseCommand
|
||||
|
||||
|
||||
def _bundled_command_classes():
|
||||
for package in (modules.commands, modules.commands.alternatives):
|
||||
for info in pkgutil.iter_modules(package.__path__):
|
||||
module = importlib.import_module(f"{package.__name__}.{info.name}")
|
||||
for _, cls in inspect.getmembers(module, inspect.isclass):
|
||||
if issubclass(cls, BaseCommand) and cls.__module__ == module.__name__:
|
||||
yield cls
|
||||
|
||||
|
||||
def test_every_bundled_get_help_text_takes_an_optional_message():
|
||||
classes = list(_bundled_command_classes())
|
||||
assert len(classes) > 40
|
||||
wrong = []
|
||||
for cls in classes:
|
||||
params = inspect.signature(cls.get_help_text).parameters
|
||||
message = params.get("message")
|
||||
if message is None or message.default is not None:
|
||||
wrong.append(cls.__name__)
|
||||
assert wrong == []
|
||||
Reference in new issue
Block a user