fylgja/src/lang/command_parser.js
2025-04-18 13:49:10 -04:00

105 lines
No EOL
2.8 KiB
JavaScript

/**
* command_parser.js
*
* Provides functionality for parsing commands for the Fylgja bot
*/
const logger = require('../utils/logger');
const FILE_NAME = 'command_parser.js';
// Import language patterns and synonyms
const commandPatterns = require('./command_patterns');
/**
* Parse a natural language command into a structured command object
*
* @param {string} commandText - The natural language command text
* @returns {Promise<Object>} Result object with success flag and parsed command or error message
*/
const parseCommand = async (commandText) => {
try {
logger.debug(`${FILE_NAME}: Parsing command: ${commandText}`);
if (!commandText || typeof commandText !== 'string') {
return {
success: false,
message: 'Empty or invalid command.'
};
}
// Convert to lowercase for case-insensitive matching
const normalizedCommand = commandText.toLowerCase().trim();
// TODO
// Handle help command separately
if (normalizedCommand === 'help') {
return {
success: true,
command: {
action: 'general',
module: 'help',
params: []
}
};
}
// Try to match command against known patterns
for (const pattern of commandPatterns) {
const match = matchPattern(normalizedCommand, pattern);
if (match) {
logger.debug(`${FILE_NAME}: Command matched pattern: ${pattern.name}`);
return {
success: true,
command: match
};
}
}
// If we reach here, no pattern matched
logger.warn(`${FILE_NAME}: No pattern matched for command: ${commandText}`);
return {
success: false,
message: "I couldn't understand that command. Try `/fylgja help` for examples."
};
} catch (error) {
logger.error(`${FILE_NAME}: Error parsing command: ${error.message}`);
logger.debug(`${FILE_NAME}: Error stack: ${error.stack}`);
return {
success: false,
message: `Error parsing command: ${error.message}`
};
}
};
/**
* Match a command against a pattern
*
* @param {string} command - The normalized command text
* @param {Object} pattern - The pattern object to match against
* @returns {Object|null} Parsed command object or null if no match
*/
const matchPattern = (command, pattern) => {
// Check if the command matches the regex pattern
const match = pattern.regex.exec(command);
if (!match) {
return null;
}
// Extract parameters based on the pattern's parameter mapping
const params = [];
for (const paramIndex of pattern.params) {
if (match[paramIndex]) {
params.push(match[paramIndex].trim());
}
}
// Return the structured command
return {
action: pattern.action,
module: pattern.module,
params
};
};
module.exports = {
parseCommand
};