From 71dbf28481e91782112dbef1b87a3d843cbdcb61 Mon Sep 17 00:00:00 2001 From: DavidDevGt Date: Wed, 6 Nov 2024 01:09:33 -0600 Subject: [PATCH] Add translation functionality for recipe instructions and enhance recipe search logic --- commands/recipes_command.js | 10 ++++-- index.js | 13 +++---- plugins/translator.js | 45 ++++++++++++++++++++++++ services/recipes_service.js | 69 ++++++++++++++++++++++++++----------- utils/recipe_utils.js | 13 +++++-- 5 files changed, 118 insertions(+), 32 deletions(-) create mode 100644 plugins/translator.js diff --git a/commands/recipes_command.js b/commands/recipes_command.js index 386a973..35f0b23 100644 --- a/commands/recipes_command.js +++ b/commands/recipes_command.js @@ -2,6 +2,7 @@ const axios = require('axios'); const { MessageMedia } = require('whatsapp-web.js'); const recipesService = require('../services/recipes_service'); const recipeUtils = require('../utils/recipe_utils'); +const { translateText } = require('../plugins/translator'); /** * Selecciona una receta aleatoria de una lista. @@ -70,7 +71,12 @@ const handleRecipeCommand = async (client, message) => { throw new Error('No se encontraron recetas para tu búsqueda.'); } - const recipeInfo = recipeUtils.formatRecipeInfo(recipe); + // Traducir instrucciones al español antes de formatear la información + const instructions = recipe.strInstructions + ? await translateText(recipe.strInstructions, 'es') + : 'No hay instrucciones disponibles.'; + + const recipeInfo = await recipeUtils.formatRecipeInfo({ ...recipe, strInstructions: instructions }); // Descargar la imagen de la receta const imageUrl = recipe.strMealThumb; @@ -92,4 +98,4 @@ const handleRecipeCommand = async (client, message) => { module.exports = { handleRecipeCommand -}; \ No newline at end of file +}; diff --git a/index.js b/index.js index ce79cce..b2781a7 100644 --- a/index.js +++ b/index.js @@ -5,6 +5,7 @@ const qrcode = require('qrcode-terminal'); const dotenv = require('dotenv'); const weatherUtils = require('./utils/weather_utils'); const weatherService = require('./services/weather_service'); +const logger = require('./utils/logger'); const { handleRecipeCommand } = require('./commands/recipes_command'); // Importar el comando de recetas const { handleWeatherCommand } = require('./commands/weather_command'); // Importar el comando de clima @@ -49,20 +50,20 @@ class WhatsAppBot { handleQR(qr) { qrcode.generate(qr, { small: true }); - console.log('🔍 Escanea el QR code con tu WhatsApp.'); + logger.info('🔍 Escanea el QR code con tu WhatsApp.'); } async handleReady() { - console.log('✅ Cliente de WhatsApp está listo!'); + logger.info('✅ Cliente de WhatsApp está listo!'); try { this.myId = this.client.info.wid._serialized; this.myNumber = this.myId.split('@')[0]; - console.log(`🔑 Tu ID de WhatsApp es: ${this.myId}`); - console.log(`📱 Tu número de teléfono es: ${this.myNumber}`); + logger.info(`🔑 Tu ID de WhatsApp es: ${this.myId}`); + logger.info(`📱 Tu número de teléfono es: ${this.myNumber}`); const chat = await this.client.getChatById(this.myId); - console.log('💭 Chat propio encontrado:', chat); + logger.info(`💭 Chat propio encontrado: ${JSON.stringify(chat, null, 2)}`); } catch (error) { - console.error(`❌ Error al obtener información: ${error}`); + logger.error(`❌ Error al obtener información: ${error}`); } } diff --git a/plugins/translator.js b/plugins/translator.js new file mode 100644 index 0000000..ed8e242 --- /dev/null +++ b/plugins/translator.js @@ -0,0 +1,45 @@ +const axios = require('axios'); +const dotenv = require('dotenv'); + +dotenv.config(); + +const apiKey = process.env.GOOGLE_GEMINI_API_KEY; + +/** + * Traduce un texto utilizando la API de Google Gemini. + * @param {String} text - Texto a traducir. + * @param {String} targetLang - Idioma de destino, 'es' para español. + * @returns {Promise} - Texto traducido. + */ +const translateText = async (text, targetLang = 'es') => { + const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent?key=${apiKey}`; + + // Añadimos el preprompt para que especifique la traducción al español + const prompt = `Por favor, traduce el siguiente texto al español:\n\n${text}`; + + try { + const response = await axios.post( + url, + { + contents: [{ parts: [{ text: prompt }] }] + }, + { + headers: { 'Content-Type': 'application/json' } + } + ); + + console.log('Respuesta de la API de traducción:', response.data); + console.log('Texto original:', text); + + // Extrae el texto traducido de la respuesta + const translatedText = response.data.candidates[0].content.parts[0].text; + return translatedText; + } catch (error) { + console.error('Error al traducir el texto:', JSON.stringify(error.response?.data, null, 2)); + return text; // Devuelve el texto original en caso de error + } +}; + +module.exports = { + translateText +}; \ No newline at end of file diff --git a/services/recipes_service.js b/services/recipes_service.js index 7c10c05..fd9993d 100644 --- a/services/recipes_service.js +++ b/services/recipes_service.js @@ -1,25 +1,59 @@ const axios = require('axios'); /** - * Busca recetas por nombre utilizando la API de TheMealDB. + * Realiza una búsqueda de recetas usando combinaciones de palabras. * @param {String} name - Nombre de la receta a buscar. * @returns {Array} - Lista de recetas encontradas. * @throws {Error} - Si no se encuentran recetas o hay un error en la solicitud. */ const searchMealByName = async (name) => { + const words = name.split(' '); + let recipes = []; + + // Intenta la búsqueda completa primero 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; // Devuelve todas las recetas encontradas + recipes = await performSearch(name); + if (recipes.length > 0) return recipes; } catch (error) { - throw new Error(`Error al buscar la receta: ${error.message}`); + console.log(`No se encontraron recetas para "${name}", probando combinaciones...`); } + + // Si no encuentra con el nombre completo, intenta con combinaciones + for (let i = 0; i < words.length; i++) { + const partialName = words.slice(i).join(' '); + try { + recipes = await performSearch(partialName); + if (recipes.length > 0) return recipes; + } catch (error) { + console.log(`No se encontraron recetas para "${partialName}"`); + } + } + + // Si no encuentra nada, intenta con cada palabra individualmente + for (const word of words) { + try { + recipes = await performSearch(word); + if (recipes.length > 0) return recipes; + } catch (error) { + console.log(`No se encontraron recetas para "${word}"`); + } + } + + // Si no se encontraron resultados, lanza un error + throw new Error('No se encontraron recetas para tu búsqueda.'); +}; + +/** + * Realiza la búsqueda en la API de TheMealDB. + * @param {String} query - Consulta de búsqueda. + * @returns {Array} - Lista de recetas encontradas. + */ +const performSearch = async (query) => { + const response = await axios.get('https://www.themealdb.com/api/json/v1/1/search.php', { params: { s: query } }); + // console.log('Request URL:', response.config.url); + // console.log('Response:', response.data); + + return response.data.meals || []; }; /** @@ -30,6 +64,7 @@ const searchMealByName = async (name) => { 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.'); } @@ -48,15 +83,11 @@ const getRandomMeal = async () => { const filterByIngredient = async (ingredient) => { try { const response = await axios.get('https://www.themealdb.com/api/json/v1/1/filter.php', { - params: { - i: ingredient - } + 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}`); @@ -75,10 +106,7 @@ const healthyCategories = ['Salad', 'Chicken', 'Eggs', 'Fish', 'Seafood', 'Pasta */ const getRandomHealthyMeal = async () => { try { - // Escoge una categoría saludable al azar const randomCategory = healthyCategories[Math.floor(Math.random() * healthyCategories.length)]; - - // Filtra las recetas por la categoría saludable seleccionada const response = await axios.get('https://www.themealdb.com/api/json/v1/1/filter.php', { params: { c: randomCategory } }); @@ -87,7 +115,6 @@ const getRandomHealthyMeal = async () => { throw new Error('No se encontraron recetas saludables.'); } - // Escoge una receta aleatoria dentro de las recetas saludables const randomMeal = response.data.meals[Math.floor(Math.random() * response.data.meals.length)]; return await lookupMealById(randomMeal.idMeal); } catch (error) { @@ -118,4 +145,4 @@ module.exports = { filterByIngredient, getRandomHealthyMeal, lookupMealById -}; \ No newline at end of file +}; diff --git a/utils/recipe_utils.js b/utils/recipe_utils.js index e8bb35a..06a175d 100644 --- a/utils/recipe_utils.js +++ b/utils/recipe_utils.js @@ -1,9 +1,16 @@ +const { translateText } = require('../plugins/translator'); + /** * 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) => { +const formatRecipeInfo = async (recipe) => { + // Traducir instrucciones de la receta + const instructions = recipe.strInstructions + ? await translateText(recipe.strInstructions, 'es') + : 'No hay instrucciones disponibles.'; + return ` 🍽️ *${recipe.strMeal}* @@ -11,7 +18,7 @@ const formatRecipeInfo = (recipe) => { 🌍 *Área:* ${recipe.strArea || 'No disponible'} 📝 *Instrucciones:* -${recipe.strInstructions || 'No hay instrucciones disponibles.'} +${instructions} 🔗 *Fuente:* ${recipe.strSource || 'No disponible'} @@ -46,4 +53,4 @@ const getIngredientsList = (recipe) => { module.exports = { formatRecipeInfo -}; \ No newline at end of file +};