mirror of
https://github.com/agessaman/meshcore-bot.git
synced 2026-10-22 14:01:14 +00:00
fix(help): preserve multiword aliases in fallback
This commit is contained in:
1 parent
3b1edea8af
commit
d3a435d9ed
4 files changed
+91
-70
No files matched your search
+25
-45
@@ -1470,6 +1470,28 @@ class CommandManager:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _find_help_command(self, lookup_name: str) -> Any | None:
|
||||
"""Resolve an exact command name or alias."""
|
||||
normalized_name = lookup_name.lower()
|
||||
command = self.commands.get(normalized_name) or self.commands.get(lookup_name)
|
||||
if command:
|
||||
return command
|
||||
|
||||
if hasattr(self, 'plugin_loader') and hasattr(self.plugin_loader, 'keyword_mappings'):
|
||||
mapped_name = self.plugin_loader.keyword_mappings.get(normalized_name)
|
||||
if mapped_name:
|
||||
command = self.commands.get(mapped_name)
|
||||
if command:
|
||||
return command
|
||||
|
||||
for cmd_instance in self.commands.values():
|
||||
if (
|
||||
hasattr(cmd_instance, 'keywords')
|
||||
and normalized_name in [keyword.lower() for keyword in cmd_instance.keywords]
|
||||
):
|
||||
return cmd_instance
|
||||
return None
|
||||
|
||||
def get_help_for_command(self, command_name: str, message: MeshMessage | None = None) -> str:
|
||||
"""Get help text for a specific command (LoRa-friendly compact format).
|
||||
|
||||
@@ -1486,60 +1508,18 @@ class CommandManager:
|
||||
return self.get_general_help(message)
|
||||
|
||||
requested_name = command_name.strip()
|
||||
# Help may include subcommand arguments (for example, ``help net
|
||||
# create``). Resolve the plugin from the first token, then pass the
|
||||
# unchanged MeshMessage to get_help_text() so the plugin can interpret
|
||||
# the remaining text in its own command-specific way.
|
||||
lookup_name = requested_name.split(maxsplit=1)[0] if requested_name else ""
|
||||
normalized_name = lookup_name.lower()
|
||||
|
||||
# First, try to find a command by exact name
|
||||
command = self.commands.get(normalized_name) or self.commands.get(lookup_name)
|
||||
command = self._find_help_command(requested_name)
|
||||
if not command and requested_name:
|
||||
command = self._find_help_command(requested_name.split(maxsplit=1)[0])
|
||||
if command:
|
||||
# Try to pass message context to get_help_text if supported
|
||||
try:
|
||||
help_text = command.get_help_text(message)
|
||||
except TypeError:
|
||||
# Fallback for commands that don't accept message parameter
|
||||
help_text = command.get_help_text()
|
||||
# Use translator if available
|
||||
if hasattr(self.bot, 'translator'):
|
||||
return self.bot.translator.translate('commands.help.specific', command=command_name, help_text=help_text)
|
||||
return f"Help {command_name}: {help_text}"
|
||||
|
||||
# Next, consult plugin_loader keyword mappings (if available)
|
||||
mapped_name: str | None = None
|
||||
if hasattr(self, 'plugin_loader') and hasattr(self.plugin_loader, 'keyword_mappings'):
|
||||
mapped_name = self.plugin_loader.keyword_mappings.get(normalized_name)
|
||||
if mapped_name:
|
||||
command = self.commands.get(mapped_name)
|
||||
if command:
|
||||
try:
|
||||
help_text = command.get_help_text(message)
|
||||
except TypeError:
|
||||
help_text = command.get_help_text()
|
||||
if hasattr(self.bot, 'translator'):
|
||||
return self.bot.translator.translate('commands.help.specific', command=command_name, help_text=help_text)
|
||||
return f"Help {command_name}: {help_text}"
|
||||
|
||||
# If still not found, search through all commands and their keywords
|
||||
for _cmd_name, cmd_instance in self.commands.items():
|
||||
# Check if the requested command name matches any of this command's keywords
|
||||
if (
|
||||
hasattr(cmd_instance, 'keywords')
|
||||
and normalized_name in [k.lower() for k in cmd_instance.keywords]
|
||||
):
|
||||
# Try to pass message context to get_help_text if supported
|
||||
try:
|
||||
help_text = cmd_instance.get_help_text(message)
|
||||
except TypeError:
|
||||
# Fallback for commands that don't accept message parameter
|
||||
help_text = cmd_instance.get_help_text()
|
||||
# Use translator if available
|
||||
if hasattr(self.bot, 'translator'):
|
||||
return self.bot.translator.translate('commands.help.specific', command=command_name, help_text=help_text)
|
||||
return f"Help {command_name}: {help_text}"
|
||||
|
||||
# If still not found, return unknown command message with helpful suggestion
|
||||
# Use the help command's method to get popular commands (only primary names, no aliases)
|
||||
available_str = ""
|
||||
|
||||
@@ -94,31 +94,36 @@ class HelpCommand(BaseCommand):
|
||||
str: The formatted help text for the specific command.
|
||||
"""
|
||||
requested_name = command_name.strip()
|
||||
lookup_name = requested_name.split(maxsplit=1)[0] if requested_name else ""
|
||||
normalized_name = lookup_name.lower()
|
||||
command = None
|
||||
candidates = [requested_name]
|
||||
if requested_name:
|
||||
base_name = requested_name.split(maxsplit=1)[0]
|
||||
if base_name != requested_name:
|
||||
candidates.append(base_name)
|
||||
|
||||
# Get the command instance by direct name first
|
||||
command = (
|
||||
self.bot.command_manager.commands.get(normalized_name)
|
||||
or self.bot.command_manager.commands.get(lookup_name)
|
||||
)
|
||||
|
||||
# Then through plugin keyword mappings (if available)
|
||||
if not command and hasattr(self.bot.command_manager, 'plugin_loader'):
|
||||
mappings = getattr(self.bot.command_manager.plugin_loader, 'keyword_mappings', {})
|
||||
mapped_name = mappings.get(normalized_name)
|
||||
if mapped_name:
|
||||
command = self.bot.command_manager.commands.get(mapped_name)
|
||||
|
||||
# Final fallback: resolve through runtime command keywords
|
||||
if not command:
|
||||
for cmd_instance in self.bot.command_manager.commands.values():
|
||||
if (
|
||||
hasattr(cmd_instance, 'keywords')
|
||||
and normalized_name in [k.lower() for k in cmd_instance.keywords]
|
||||
):
|
||||
command = cmd_instance
|
||||
break
|
||||
mappings = getattr(self.bot.command_manager.plugin_loader, 'keyword_mappings', {})
|
||||
for lookup_name in candidates:
|
||||
normalized_name = lookup_name.lower()
|
||||
command = (
|
||||
self.bot.command_manager.commands.get(normalized_name)
|
||||
or self.bot.command_manager.commands.get(lookup_name)
|
||||
)
|
||||
if not command:
|
||||
mapped_name = mappings.get(normalized_name)
|
||||
if mapped_name:
|
||||
command = self.bot.command_manager.commands.get(mapped_name)
|
||||
if not command:
|
||||
command = next(
|
||||
(
|
||||
cmd_instance
|
||||
for cmd_instance in self.bot.command_manager.commands.values()
|
||||
if hasattr(cmd_instance, 'keywords')
|
||||
and normalized_name in [keyword.lower() for keyword in cmd_instance.keywords]
|
||||
),
|
||||
None,
|
||||
)
|
||||
if command:
|
||||
break
|
||||
|
||||
if command:
|
||||
# Pass message context to get_help_text if the method supports it
|
||||
@@ -314,4 +319,3 @@ class HelpCommand(BaseCommand):
|
||||
return ', '.join(result) + suffix
|
||||
break
|
||||
return ', '.join(result)
|
||||
|
||||
@@ -362,6 +362,24 @@ class TestGetHelpForCommand:
|
||||
mock_cmd.get_help_text.assert_called_once_with(message)
|
||||
assert message.content == "help net create"
|
||||
|
||||
def test_multiword_alias_is_checked_before_subcommand_fallback(self, cm_bot):
|
||||
dadjoke_cmd = MagicMock()
|
||||
dadjoke_cmd.keywords = ["dadjoke", "dad joke"]
|
||||
dadjoke_cmd.get_help_text = Mock(return_value="Dad joke help")
|
||||
unrelated_cmd = MagicMock()
|
||||
unrelated_cmd.keywords = ["dad"]
|
||||
unrelated_cmd.get_help_text = Mock(return_value="Wrong help")
|
||||
manager = make_manager(
|
||||
cm_bot,
|
||||
commands={"dadjoke": dadjoke_cmd, "dad": unrelated_cmd},
|
||||
)
|
||||
|
||||
result = manager.get_help_for_command("dad joke")
|
||||
|
||||
assert "Dad joke help" in result
|
||||
dadjoke_cmd.get_help_text.assert_called_once()
|
||||
unrelated_cmd.get_help_text.assert_not_called()
|
||||
|
||||
|
||||
class TestInternetStatusCache:
|
||||
"""Tests for InternetStatusCache."""
|
||||
|
||||
@@ -253,6 +253,25 @@ class TestGetSpecificHelp:
|
||||
result = cmd.get_specific_help("sched")
|
||||
assert isinstance(result, str)
|
||||
|
||||
def test_multiword_alias_is_checked_before_subcommand_fallback(self):
|
||||
bot = _make_bot()
|
||||
dadjoke_cmd = MagicMock()
|
||||
dadjoke_cmd.get_help_text = Mock(return_value="Dad joke help")
|
||||
dadjoke_cmd.keywords = ["dadjoke", "dad joke"]
|
||||
unrelated_cmd = MagicMock()
|
||||
unrelated_cmd.get_help_text = Mock(return_value="Wrong help")
|
||||
unrelated_cmd.keywords = ["dad"]
|
||||
bot.command_manager.commands = {
|
||||
"dadjoke": dadjoke_cmd,
|
||||
"dad": unrelated_cmd,
|
||||
}
|
||||
cmd = HelpCommand(bot)
|
||||
|
||||
cmd.get_specific_help("dad joke")
|
||||
|
||||
dadjoke_cmd.get_help_text.assert_called_once()
|
||||
unrelated_cmd.get_help_text.assert_not_called()
|
||||
|
||||
def test_no_get_help_text_attribute(self):
|
||||
"""Command without get_help_text returns no_help key."""
|
||||
bot = _make_bot()
|
||||
|
||||
Reference in new issue
Block a user