Implement case-insensitive command processing across all bot modules

Added normalize_input() function to BaseModule for consistent lowercase conversion of user input. Updated all command modules to use normalization for commands, arguments, pet names, location names, gym names, and item names. Players can now use any capitalization for commands and arguments.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
megaproxy 2025-07-14 21:57:51 +01:00
parent 39ba55832d
commit 8e9ff2960f
8 changed files with 93 additions and 16 deletions

View file

@ -12,6 +12,15 @@ class BaseModule(ABC):
self.database = database
self.game_engine = game_engine
@staticmethod
def normalize_input(user_input):
"""Normalize user input by converting to lowercase for case-insensitive command processing"""
if isinstance(user_input, str):
return user_input.lower()
elif isinstance(user_input, list):
return [item.lower() if isinstance(item, str) else item for item in user_input]
return user_input
@abstractmethod
def get_commands(self):
"""Return list of commands this module handles"""

View file

@ -87,7 +87,7 @@ class BattleSystem(BaseModule):
if not player:
return
move_name = " ".join(args).title() # Normalize to Title Case
move_name = " ".join(self.normalize_input(args)).title() # Normalize to Title Case
result = await self.game_engine.battle_engine.execute_battle_turn(player["id"], move_name)
if "error" in result:

View file

@ -64,7 +64,7 @@ class Exploration(BaseModule):
return
# Handle various input formats and normalize location names
destination_input = " ".join(args).lower()
destination_input = self.normalize_input(" ".join(args))
# Map common variations to exact location names
location_mappings = {
@ -82,7 +82,7 @@ class Exploration(BaseModule):
destination = location_mappings.get(destination_input)
if not destination:
# Fall back to title case if no mapping found
destination = " ".join(args).title()
destination = " ".join(self.normalize_input(args)).title()
location = await self.database.get_location_by_name(destination)
@ -171,7 +171,7 @@ class Exploration(BaseModule):
if args:
# Specific location requested
location_name = " ".join(args).title()
location_name = " ".join(self.normalize_input(args)).title()
else:
# Default to current location
current_location = await self.database.get_player_location(player["id"])

View file

@ -13,13 +13,13 @@ class GymBattles(BaseModule):
if command == "gym":
if not args:
await self.cmd_gym_list(channel, nickname)
elif args[0] == "list":
elif self.normalize_input(args[0]) == "list":
await self.cmd_gym_list_all(channel, nickname)
elif args[0] == "challenge":
elif self.normalize_input(args[0]) == "challenge":
await self.cmd_gym_challenge(channel, nickname, args[1:])
elif args[0] == "info":
elif self.normalize_input(args[0]) == "info":
await self.cmd_gym_info(channel, nickname, args[1:])
elif args[0] == "status":
elif self.normalize_input(args[0]) == "status":
await self.cmd_gym_status(channel, nickname)
else:
await self.cmd_gym_list(channel, nickname)
@ -111,7 +111,7 @@ class GymBattles(BaseModule):
self.send_message(channel, f"{nickname}: You are not in a valid location! Use !travel to go somewhere first.")
return
gym_name = " ".join(args).strip('"')
gym_name = " ".join(self.normalize_input(args)).strip('"')
# Look for gym in player's current location (case-insensitive)
gym = await self.database.get_gym_by_name_in_location(gym_name, location["id"])
@ -266,7 +266,7 @@ class GymBattles(BaseModule):
if not player:
return
gym_name = " ".join(args).strip('"')
gym_name = " ".join(self.normalize_input(args)).strip('"')
# First try to find gym in player's current location
location = await self.database.get_player_location(player["id"])

View file

@ -72,7 +72,7 @@ class Inventory(BaseModule):
if not player:
return
item_name = " ".join(args)
item_name = " ".join(self.normalize_input(args))
result = await self.database.use_item(player["id"], item_name)
if not result["success"]:

View file

@ -88,7 +88,7 @@ class PetManagement(BaseModule):
if not player:
return
pet_name = " ".join(args)
pet_name = " ".join(self.normalize_input(args))
result = await self.database.activate_pet(player["id"], pet_name)
if result["success"]:
@ -112,7 +112,7 @@ class PetManagement(BaseModule):
if not player:
return
pet_name = " ".join(args)
pet_name = " ".join(self.normalize_input(args))
result = await self.database.deactivate_pet(player["id"], pet_name)
if result["success"]:
@ -174,7 +174,7 @@ class PetManagement(BaseModule):
return
# Split args into pet identifier and new nickname
pet_identifier = args[0]
pet_identifier = self.normalize_input(args[0])
new_nickname = " ".join(args[1:])
result = await self.database.set_pet_nickname(player["id"], pet_identifier, new_nickname)

View file

@ -303,12 +303,14 @@ class PetBotDebug:
self.handle_command(channel, nickname, message)
def handle_command(self, channel, nickname, message):
from modules.base_module import BaseModule
command_parts = message[1:].split()
if not command_parts:
return
command = command_parts[0].lower()
args = command_parts[1:]
command = BaseModule.normalize_input(command_parts[0])
args = BaseModule.normalize_input(command_parts[1:])
try:
if command in self.command_map:

View file

@ -822,6 +822,32 @@ class PetBotRequestHandler(BaseHTTPRequestHandler):
border: 1px solid var(--border-color);
}}
.hidden-spawn {{
display: none;
}}
.more-button {{
background: var(--gradient-primary) !important;
color: white !important;
cursor: pointer;
transition: transform 0.2s ease;
}}
.more-button:hover {{
transform: scale(1.05);
}}
.less-button {{
background: #ff6b6b !important;
color: white !important;
cursor: pointer;
transition: transform 0.2s ease;
}}
.less-button:hover {{
transform: scale(1.05);
}}
.info-section {{
background: var(--bg-secondary);
border-radius: 15px;
@ -862,6 +888,46 @@ class PetBotRequestHandler(BaseHTTPRequestHandler):
💡 Use <code>!wild &lt;location&gt;</code> in #petz to see what pets spawn in a specific area
</p>
</div>
<script>
function toggleSpawns(locationId) {{
const hiddenSpawns = document.querySelectorAll(`#hidden-${{locationId}}`);
const moreButton = document.querySelector(`[onclick="toggleSpawns(${{locationId}})"]`);
if (!moreButton) return;
const isHidden = hiddenSpawns[0] && hiddenSpawns[0].style.display === 'none' || hiddenSpawns[0] && hiddenSpawns[0].classList.contains('hidden-spawn');
if (isHidden) {{
// Show hidden spawns
hiddenSpawns.forEach(spawn => {{
spawn.classList.remove('hidden-spawn');
spawn.style.display = 'inline-block';
}});
moreButton.textContent = 'Show less';
moreButton.className = 'spawn-badge less-button';
moreButton.setAttribute('onclick', `hideSpawns(${{locationId}})`);
}}
}}
function hideSpawns(locationId) {{
const hiddenSpawns = document.querySelectorAll(`#hidden-${{locationId}}`);
const lessButton = document.querySelector(`[onclick="hideSpawns(${{locationId}})"]`);
if (!lessButton) return;
// Hide spawns again
hiddenSpawns.forEach(spawn => {{
spawn.classList.add('hidden-spawn');
spawn.style.display = 'none';
}});
const hiddenCount = hiddenSpawns.length;
lessButton.textContent = `+${{hiddenCount}} more`;
lessButton.className = 'spawn-badge more-button';
lessButton.setAttribute('onclick', `toggleSpawns(${{locationId}})`);
}}
</script>
</body>
</html>"""