Refactor: Implement generalized command argument parsing with optional quotes
This commit introduces a significant refactoring to how command arguments are parsed. The primary goal is to allow users to optionally enclose arguments containing spaces in double quotes for all commands, providing a more flexible and intuitive command input experience.
Key changes include:
1. **Centralized Argument Parsing (`src/queue/server.py`):**
* The `process_chat_command` function in `src/queue/server.py` now handles the primary parsing of command arguments.
* Incoming raw arguments (previously a list of strings split by spaces) are joined into a single string.
* This string is then processed using the regular expression `re.findall(r'"([^"]*)"|(\S+)', ...)` to accurately identify and separate arguments. This regex correctly distinguishes between:
* Substrings enclosed in double quotes (e.g., `"my poll question"`), which are treated as single arguments.
* Sequences of non-whitespace characters (e.g., `1`, `myoption`), also treated as single arguments.
* This ensures that all command handlers receive a list of arguments (`parsed_args`) where multi-word arguments (if originally quoted) are correctly preserved as single elements.
* A correction was made to the regex pattern: an initial version mistakenly used `(\\\\S+)` (matching a literal backslash then 'S') instead of the intended `(\\S+)` (matching any non-whitespace sequence) for unquoted arguments. This fix ensures that simple unquoted arguments like numbers (e.g., for the `!vote` command) are parsed correctly.
* The `re` module import was ensured.
2. **Simplification of Poll Command (`src/features/examples.py`):**
* The `cmd_start_poll` method in `src/features/examples.py` has been simplified. Its specific regex-based logic for parsing quoted poll questions and options was removed.
* This method now relies on the generalized, upstream parsing performed by `process_chat_command` in `src/queue/server.py`. It expects to receive a list of arguments that have already been correctly parsed.
* Test calls for `cmd_start_poll` within the `if __name__ == '__main__':` block were updated to align with this change, demonstrating how arguments (like poll questions and options) are now passed as a simple list of strings.
These changes collectively improve the robustness and usability of the command system by standardizing argument parsing and allowing for more complex argument values.
This commit is contained in:
parent
c43cb5e0ce
commit
c17f382c60
5 changed files with 665 additions and 204 deletions
75
main.py
75
main.py
|
|
@ -205,11 +205,12 @@ def main() -> None:
|
|||
client_secret=client_secret,
|
||||
channel=channel,
|
||||
use_queue=args.use_queue,
|
||||
command_prefix=config.get("command_prefix", "!"),
|
||||
token_cache_file=args.token_cache,
|
||||
admin_users=admin_users,
|
||||
force_new_token=args.force_new_token,
|
||||
access_token=None, # Explicitly set to None to avoid using env vars
|
||||
refresh_token=None # Explicitly set to None to avoid using env vars
|
||||
refresh_token=None # Explicitly set to None
|
||||
)
|
||||
|
||||
# Register basic commands
|
||||
|
|
@ -246,17 +247,71 @@ def main() -> None:
|
|||
if args.advanced:
|
||||
try:
|
||||
from src.features.examples import AdvancedExamples
|
||||
import traceback # For more detailed error logging
|
||||
print("Enabling advanced features...")
|
||||
advanced: AdvancedExamples = AdvancedExamples(bot)
|
||||
print("Advanced features enabled: polls, voting, timers, points system")
|
||||
if os.name == 'nt':
|
||||
print("Sound effects enabled (Windows only)")
|
||||
# Create sounds directory if it doesn't exist
|
||||
if not os.path.exists("sounds"):
|
||||
os.makedirs("sounds")
|
||||
print("Created 'sounds' directory. Add .wav files to use with !sound command")
|
||||
|
||||
if args.use_queue:
|
||||
from src.queue.server import setup_advanced_commands_config
|
||||
setup_advanced_commands_config(
|
||||
bot_username_main=bot.username,
|
||||
channel_main=channel,
|
||||
admin_users_main=admin_users if admin_users else [],
|
||||
oauth_token_main=bot.access_token if bot.access_token else ""
|
||||
)
|
||||
print("Advanced features configuration (poll, points, etc.) saved for queue processing.")
|
||||
print("The queue consumer will register these commands: !poll, !vote, !endpoll, !points, !givepoints.")
|
||||
|
||||
else: # Not using queue, direct registration with bot instance
|
||||
|
||||
# Wrapper for send_message_func for AdvancedExamples
|
||||
# Expected: (channel, oauth_token, bot_username, message)
|
||||
def non_queue_send_message_wrapper(ch_ignored: str, oauth_ignored: Optional[str], bot_user_ignored: str, message: str):
|
||||
bot.send_message(message) # bot.send_message handles its own channel context
|
||||
|
||||
# Wrapper for is_admin_func for AdvancedExamples
|
||||
# Expected: (username, channel, admin_list)
|
||||
def non_queue_is_admin_wrapper(usr: str, chan_ignored: str, admin_list_ignored: List[str]) -> bool:
|
||||
return bot.is_admin(usr) # bot.is_admin uses its own channel and admin_list
|
||||
|
||||
advanced_instance = AdvancedExamples(
|
||||
send_message_func=non_queue_send_message_wrapper,
|
||||
is_admin_func=non_queue_is_admin_wrapper,
|
||||
bot_username=bot.username,
|
||||
channel=channel, # Pass the specific channel to AdvancedExamples
|
||||
admin_users=admin_users if admin_users else [], # Pass specific admin_users
|
||||
use_queue=False
|
||||
)
|
||||
|
||||
# Register commands with the bot instance
|
||||
# Lambdas pass username, args, and the bot's current access_token
|
||||
bot.register_command("poll", lambda u, a, b_inst: advanced_instance.cmd_start_poll(u, a, b_inst.access_token))
|
||||
bot.register_command("vote", lambda u, a, b_inst: advanced_instance.cmd_vote(u, a, b_inst.access_token))
|
||||
bot.register_command("endpoll", lambda u, a, b_inst: advanced_instance.cmd_end_poll(u, a, b_inst.access_token))
|
||||
bot.register_command("points", lambda u, a, b_inst: advanced_instance.cmd_points(u, a, b_inst.access_token))
|
||||
# Using "givepoints" for consistency with queue registration
|
||||
bot.register_command("givepoints", lambda u, a, b_inst: advanced_instance.cmd_give_points(u, a, b_inst.access_token))
|
||||
|
||||
print("Advanced features enabled directly: polls, voting, points system.")
|
||||
print("Use !poll, !vote, !endpoll, !points, !givepoints.")
|
||||
|
||||
# Timer and Sound commands are handled by AdvancedExamples based on its use_queue flag
|
||||
# For non-queue mode, AdvancedExamples will allow them.
|
||||
bot.register_command("timer", lambda u, a, b_inst: advanced_instance.cmd_timer(u,a,b_inst.access_token))
|
||||
print("Timer command !timer enabled.")
|
||||
if os.name == 'nt':
|
||||
bot.register_command("sound", lambda u, a, b_inst: advanced_instance.cmd_play_sound(u, a, b_inst.access_token))
|
||||
print("Sound effects !sound enabled (Windows only).")
|
||||
# Create sounds directory if it doesn't exist (moved from previous unconditional spot)
|
||||
if not os.path.exists("sounds"):
|
||||
os.makedirs("sounds")
|
||||
print("Created 'sounds' directory. Add .wav files to use with !sound command")
|
||||
|
||||
except ImportError as e:
|
||||
print(f"Failed to import advanced features: {e}")
|
||||
print(f"Failed to import or configure advanced features: {e}")
|
||||
print(traceback.format_exc()) # Print full traceback
|
||||
except Exception as e:
|
||||
print(f"Error setting up advanced features: {e}")
|
||||
print(traceback.format_exc()) # Print full traceback
|
||||
|
||||
# Enable game control if requested
|
||||
if args.game_control:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue