diff --git a/commands/recipes_command.js b/commands/recipes_command.js new file mode 100644 index 0000000..44542fb --- /dev/null +++ b/commands/recipes_command.js @@ -0,0 +1,41 @@ +const recipesService = require('../services/recipes_service'); +const recipeUtils = require('../utils/recipe_utils'); + +/** + * Maneja el comando de recetas. + * @param {Object} client - Instancia del cliente de WhatsApp. + * @param {Object} message - Mensaje recibido. + */ +const handleRecipeCommand = async (client, message) => { + const parts = message.body.trim().split(' '); + const subCommand = parts[0].toLowerCase(); + const query = parts.slice(1).join(' '); + + try { + let recipe; + + if (subCommand === '!receta') { + if (query) { + // Buscar receta por nombre + recipe = await recipesService.searchMealByName(query); + } else { + // Obtener receta aleatoria + recipe = await recipesService.getRandomMeal(); + } + + const recipeInfo = recipeUtils.formatRecipeInfo(recipe); + await client.sendMessage(message.from, recipeInfo); + console.log(`📤 Información de la receta enviada a ${message.from}`); + } else { + // Manejar otros subcomandos si es necesario + await client.sendMessage(message.from, '❌ Comando no reconocido.'); + } + } catch (error) { + console.error('❌ Error al manejar el comando de recetas:', error); + await client.sendMessage(message.from, `❌ Error al obtener la receta: ${error.message}`); + } +}; + +module.exports = { + handleRecipeCommand +}; \ No newline at end of file diff --git a/commands/weather_command.js b/commands/weather_command.js new file mode 100644 index 0000000..4f4507d --- /dev/null +++ b/commands/weather_command.js @@ -0,0 +1,48 @@ +const weatherService = require('../services/weather_service'); +const weatherUtils = require('../utils/weather_utils'); + +/** + * Maneja el comando de clima. + * @param {Client} client - Instancia del cliente de WhatsApp. + * @param {Object} message - Mensaje recibido. + */ +const handleWeatherCommand = async (client, message) => { + const parts = message.body.trim().split(' '); + const command = parts[0].toLowerCase(); + const city = parts.length > 1 ? parts.slice(1).join(' ') : 'Guatemala'; + + try { + const { latitude, longitude } = await weatherUtils.getCoordinates(city); + const weather = await weatherService.getWeather(latitude, longitude); + const weatherDescription = weatherUtils.translateWeatherCode(weather.weathercode); + + const weatherInfo = formatWeatherInfo(city, weather, weatherDescription); + await client.sendMessage(message.from, weatherInfo); + console.log(`📤 Información del clima de ${city} enviada a ${message.from}`); + } catch (error) { + console.error('❌ Error al obtener el clima:', error); + await client.sendMessage(message.from, `❌ Error al obtener el clima: ${error.message}`); + } +}; + +/** + * Formatea la información del clima para enviarla como mensaje. + * @param {String} city - Nombre de la ciudad. + * @param {Object} weather - Datos del clima obtenidos del servicio. + * @param {String} weatherDescription - Descripción traducida del código de clima. + * @returns {String} - Mensaje formateado con la información del clima. + */ +const formatWeatherInfo = (city, weather, weatherDescription) => { + return ` +🌤️ *Clima Actual en ${city}*: +- 🌡️ Temperatura: ${weather.temperature}°C +- 💨 Viento: ${weather.windspeed} km/h +- 🧭 Dirección del viento: ${weather.winddirection}° +- ☁️ Condición: ${weatherDescription} +- ⏰ Hora: ${new Date(weather.time).toLocaleTimeString('es-ES')} + `.trim(); +}; + +module.exports = { + handleWeatherCommand +}; \ No newline at end of file diff --git a/index.js b/index.js index bf6f29e..ce79cce 100644 --- a/index.js +++ b/index.js @@ -1,8 +1,12 @@ +// index.js + const { Client, LocalAuth } = require('whatsapp-web.js'); const qrcode = require('qrcode-terminal'); const dotenv = require('dotenv'); const weatherUtils = require('./utils/weather_utils'); const weatherService = require('./services/weather_service'); +const { handleRecipeCommand } = require('./commands/recipes_command'); // Importar el comando de recetas +const { handleWeatherCommand } = require('./commands/weather_command'); // Importar el comando de clima // Configuración inicial dotenv.config(); @@ -80,11 +84,13 @@ class WhatsAppBot { }); const command = message.body.trim().toLowerCase(); - + if (command === '!ping') { await this.handlePingCommand(message); } else if (command.startsWith('!clima')) { - await this.handleWeatherCommand(message); + await handleWeatherCommand(this.client, message); + } else if (command.startsWith('!receta')) { + await handleRecipeCommand(this.client, message); } } @@ -97,35 +103,6 @@ class WhatsAppBot { } } - async handleWeatherCommand(message) { - const parts = message.body.split(' '); - const city = parts.length > 1 ? parts.slice(1).join(' ') : 'Guatemala'; - - try { - const { latitude, longitude } = await weatherUtils.getCoordinates(city); - const weather = await weatherService.getWeather(latitude, longitude); - const weatherDescription = weatherUtils.translateWeatherCode(weather.weathercode); - - const weatherInfo = this.formatWeatherInfo(city, weather, weatherDescription); - await this.client.sendMessage(message.from, weatherInfo); - console.log(`📤 Información del clima de ${city} enviada a ${message.from}`); - } catch (error) { - console.error('❌ Error al obtener el clima:', error); - await this.client.sendMessage(message.from, `❌ Error al obtener el clima: ${error.message}`); - } - } - - formatWeatherInfo(city, weather, weatherDescription) { - return ` -🌤️ *Clima Actual en ${city}*: -- 🌡️ Temperatura: ${weather.temperature}°C -- 💨 Viento: ${weather.windspeed} km/h -- 🧭 Dirección del viento: ${weather.winddirection}° -- ☁️ Condición: ${weatherDescription} -- ⏰ Hora: ${new Date(weather.time).toLocaleTimeString('es-ES')} - `.trim(); - } - logMessage(message) { console.log('📩 Mensaje recibido:', { from: message.from, @@ -145,4 +122,4 @@ class WhatsAppBot { // Iniciar el bot const bot = new WhatsAppBot(); -bot.start(); \ No newline at end of file +bot.start(); diff --git a/services/recipes_service.js b/services/recipes_service.js new file mode 100644 index 0000000..f12658f --- /dev/null +++ b/services/recipes_service.js @@ -0,0 +1,72 @@ +const axios = require('axios'); + +/** + * Busca una receta por nombre utilizando la API de TheMealDB. + * @param {String} name - Nombre de la receta a buscar. + * @returns {Object} - Objeto de receta encontrado. + * @throws {Error} - Si no se encuentra la receta o hay un error en la solicitud. + */ +const searchMealByName = async (name) => { + try { + const response = await axios.get('https://www.themealdb.com/api/json/v1/1/search.php', { + params: { + s: name + } + }); + + if (!response.data.meals) { + throw new Error('No se encontraron recetas para tu búsqueda.'); + } + + return response.data.meals[0]; // Retorna la primera receta encontrada + } catch (error) { + throw new Error(`Error al buscar la receta: ${error.message}`); + } +}; + +/** + * Obtiene una receta aleatoria utilizando la API de TheMealDB. + * @returns {Object} - Objeto de receta aleatoria. + * @throws {Error} - Si hay un error en la solicitud. + */ +const getRandomMeal = async () => { + try { + const response = await axios.get('https://www.themealdb.com/api/json/v1/1/random.php'); + if (!response.data.meals) { + throw new Error('No se pudo obtener una receta aleatoria.'); + } + return response.data.meals[0]; + } catch (error) { + throw new Error(`Error al obtener una receta aleatoria: ${error.message}`); + } +}; + +/** + * Filtra recetas por ingrediente principal utilizando la API de TheMealDB. + * @param {String} ingredient - Ingrediente principal para filtrar recetas. + * @returns {Array} - Lista de recetas que contienen el ingrediente especificado. + * @throws {Error} - Si no se encuentran recetas o hay un error en la solicitud. + */ +const filterByIngredient = async (ingredient) => { + try { + const response = await axios.get('https://www.themealdb.com/api/json/v1/1/filter.php', { + params: { + i: ingredient + } + }); + + if (!response.data.meals) { + throw new Error('No se encontraron recetas con ese ingrediente.'); + } + + return response.data.meals; + } catch (error) { + throw new Error(`Error al filtrar por ingrediente: ${error.message}`); + } +}; + +module.exports = { + searchMealByName, + getRandomMeal, + filterByIngredient +}; diff --git a/utils/recipe_utils.js b/utils/recipe_utils.js new file mode 100644 index 0000000..e8bb35a --- /dev/null +++ b/utils/recipe_utils.js @@ -0,0 +1,49 @@ +/** + * Formatea la información de una receta para enviarla como mensaje. + * @param {Object} recipe - Objeto de receta obtenido de TheMealDB. + * @returns {String} - Mensaje formateado con la información de la receta. + */ +const formatRecipeInfo = (recipe) => { + return ` +🍽️ *${recipe.strMeal}* + +📄 *Categoría:* ${recipe.strCategory || 'No disponible'} +🌍 *Área:* ${recipe.strArea || 'No disponible'} + +📝 *Instrucciones:* +${recipe.strInstructions || 'No hay instrucciones disponibles.'} + +🔗 *Fuente:* +${recipe.strSource || 'No disponible'} + +🌐 *Video:* +${recipe.strYoutube || 'No disponible'} + +🌐 *Imagen:* +${recipe.strMealThumb} + +🛒 *Ingredientes:* +${getIngredientsList(recipe)} + `.trim(); +}; + +/** + * Genera una lista de ingredientes y sus medidas. + * @param {Object} recipe - Objeto de receta obtenido de TheMealDB. + * @returns {String} - Lista formateada de ingredientes. + */ +const getIngredientsList = (recipe) => { + let ingredients = ''; + for (let i = 1; i <= 20; i++) { + const ingredient = recipe[`strIngredient${i}`]; + const measure = recipe[`strMeasure${i}`]; + if (ingredient && ingredient.trim() !== '') { + ingredients += `- ${ingredient} - ${measure}\n`; + } + } + return ingredients || 'No hay ingredientes disponibles.'; +}; + +module.exports = { + formatRecipeInfo +}; \ No newline at end of file