From 16980a8c17945484223fe845aba6cf8325118bf5 Mon Sep 17 00:00:00 2001 From: DavidDevGt Date: Mon, 11 Nov 2024 07:40:48 +0000 Subject: [PATCH] little update --- .gitignore | 3 +- commands/guide_command.js | 60 +++++++++++++++++++++++++++++++++++ commands/stoicism_command.js | 21 +++++++++++++ database/.gitkeep | 0 index.js | 42 ++++++++++++++++--------- plugins/brainstormer.js | 48 ++++++++++++++++++++++++++++ plugins/email_draft.js | 49 +++++++++++++++++++++++++++++ plugins/formatter.js | 49 +++++++++++++++++++++++++++++ plugins/translator.js | 6 +++- services/cron_service.js | 61 ++++++++++-------------------------- tasks/stoicism_reminder.js | 50 +++++++++++++++++++++++++++++ 11 files changed, 329 insertions(+), 60 deletions(-) create mode 100644 commands/guide_command.js create mode 100644 commands/stoicism_command.js create mode 100644 database/.gitkeep create mode 100644 plugins/brainstormer.js create mode 100644 plugins/email_draft.js create mode 100644 plugins/formatter.js create mode 100644 tasks/stoicism_reminder.js diff --git a/.gitignore b/.gitignore index db8fd0b..64e8852 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ bot.log .wwebjs_cache node_modules temp -config.json \ No newline at end of file +config.json +database/ubications.json \ No newline at end of file diff --git a/commands/guide_command.js b/commands/guide_command.js new file mode 100644 index 0000000..05496c0 --- /dev/null +++ b/commands/guide_command.js @@ -0,0 +1,60 @@ +const fs = require('fs'); +const path = require('path'); + +// Cargar ubicaciones guardadas desde el archivo JSON +const locationsFilePath = path.join(__dirname, '../database/ubications.json'); +let destinationLocation = null; // Variable para guardar la ubicación deseada temporalmente + +function handleGuideCommand(client, message) { + const commandParts = message.body.trim().split(' '); + + if (commandParts.length < 2) { + message.reply('❌ Por favor, especifica la ubicación deseada. Ejemplo: !guiaciudad casa'); + return; + } + + const locationName = commandParts[1]; + let locations; + + try { + locations = JSON.parse(fs.readFileSync(locationsFilePath, 'utf8')); + } catch (error) { + message.reply('❌ No se pudo leer el archivo de ubicaciones. Asegúrate de que exista y esté en formato JSON.'); + console.error('Error leyendo ubicaciones:', error); + return; + } + + if (!locations[locationName]) { + message.reply(`❌ Ubicación "${locationName}" no encontrada en tu base de datos.`); + return; + } + + // Guardar la ubicación deseada temporalmente + destinationLocation = locations[locationName]; + message.reply(`📍 Ubicación "${locationName}" encontrada. Por favor, envía tu ubicación actual para planificar la ruta.`); +} + +function handleLocationMessage(client, message) { + if (destinationLocation) { + const currentLatitude = message.location.latitude; + const currentLongitude = message.location.longitude; + const destinationLatitude = destinationLocation.latitude; + const destinationLongitude = destinationLocation.longitude; + + // Generar el enlace de Google Maps para la ruta + const mapsUrl = `https://www.google.com/maps/dir/?api=1&origin=${currentLatitude},${currentLongitude}&destination=${destinationLatitude},${destinationLongitude}`; + + // Responder con el enlace de Google Maps + message.reply(`🗺️ Aquí tienes la ruta a la ubicación deseada: ${mapsUrl}`); + + // Limpiar la ubicación de destino para la próxima vez + destinationLocation = null; + } else { + message.reply('❌ No has especificado una ubicación de destino. Usa el comando !guiaciudad {ubicacion_deseada} primero.'); + } +} + +module.exports = { + handleGuideCommand, + handleLocationMessage +}; diff --git a/commands/stoicism_command.js b/commands/stoicism_command.js new file mode 100644 index 0000000..88e00e2 --- /dev/null +++ b/commands/stoicism_command.js @@ -0,0 +1,21 @@ +const { getStoicQuote } = require('../tasks/stoicism_reminder'); + +/** + * Maneja el comando `!estoico` para enviar una cita de estoicismo en español. + * @param {Object} client - Instancia del cliente de WhatsApp. + * @param {Object} message - Mensaje recibido de WhatsApp. + */ +const handleStoicismCommand = async (client, message) => { + try { + const quoteMessage = await getStoicQuote(); + await client.sendMessage(message.from, quoteMessage); + console.log(`📤 Cita de estoicismo enviada en respuesta a ${message.from}`); + } catch (error) { + console.error('❌ Error al enviar la cita de estoicismo:', error); + await client.sendMessage(message.from, '❌ Hubo un problema al obtener la cita de estoicismo. Intenta más tarde.'); + } +}; + +module.exports = { + handleStoicismCommand +}; diff --git a/database/.gitkeep b/database/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/index.js b/index.js index 043a221..53c0938 100644 --- a/index.js +++ b/index.js @@ -7,6 +7,8 @@ const logger = require('./utils/logger'); const { handleRecipeCommand } = require('./commands/recipes_command'); const { handleWeatherCommand } = require('./commands/weather_command'); const { initializeCronJobs } = require('./services/cron_service'); +const { handleGuideCommand, handleLocationMessage } = require('./commands/guide_command'); +const { handleStoicismCommand } = require('./commands/stoicism_command'); dotenv.config(); @@ -57,10 +59,11 @@ class WhatsAppBot { this.myNumber = this.myId.split('@')[0]; logger.info(`🔑 Tu ID de WhatsApp es: ${this.myId}`); logger.info(`📱 Tu número de teléfono es: ${this.myNumber}`); - const initCron = await initializeCronJobs(this.client); - if (initCron) logger.info('⏰ Tareas programadas inicializadas correctamente'); + + initializeCronJobs(this.client); + const chat = await this.client.getChatById(this.myId); - logger.info(`💭 Chat propio encontrado: ${JSON.stringify(chat, null, 2)}`); + //logger.info(`💭 Chat propio encontrado: ${JSON.stringify(chat, null, 2)}`); } catch (error) { logger.error(`❌ Error al obtener información: ${error}`); } @@ -74,7 +77,12 @@ class WhatsAppBot { async handleMessage(message) { if (!this.myId || !this.isChatAllowed(message.from)) return; - + + if (message.type === 'location') { + handleLocationMessage(this.client, message); + return; + } + console.log('📨 Mensaje creado:', { from: message.from, fromMe: message.fromMe, @@ -82,21 +90,27 @@ class WhatsAppBot { type: message.type, timestamp: message.timestamp }); - + const command = message.body.trim().toLowerCase(); - - if (command === '!ping') { - await this.handlePingCommand(message); - } else if (command.startsWith('!clima')) { - await handleWeatherCommand(this.client, message); - } else if (command.startsWith('!receta')) { - await handleRecipeCommand(this.client, message); + + const commands = { + '!ping': () => this.handlePingCommand(message), + '!clima': () => handleWeatherCommand(this.client, message), + '!receta': () => handleRecipeCommand(this.client, message), + '!guiaciudad': () => handleGuideCommand(this.client, message), + '!calm': () => handleStoicismCommand(this.client, message) + }; + + const commandHandler = Object.entries(commands).find(([key]) => command.startsWith(key)); + + if (commandHandler) { + await commandHandler[1](); } - } + } async handlePingCommand(message) { try { - await this.client.sendMessage(message.from, 'pong'); + await this.client.sendMessage(message.from, 'pong madafaka'); console.log(`📤 Respondido 'pong' al número autorizado ${message.from}`); } catch (error) { console.error('❌ Error al enviar respuesta:', error); diff --git a/plugins/brainstormer.js b/plugins/brainstormer.js new file mode 100644 index 0000000..8a2a502 --- /dev/null +++ b/plugins/brainstormer.js @@ -0,0 +1,48 @@ +const axios = require('axios'); +const dotenv = require('dotenv'); + +dotenv.config(); + +const apiKey = process.env.GOOGLE_GEMINI_API_KEY; + +/** + * Genera una lista de ideas creativas para un tema dado. + * @param {String} topic - Tema para el cual generar ideas. + * @returns {Promise} - Lista de ideas creativas. + */ +const brainstormIdeas = async (topic) => { + const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent?key=${apiKey}`; + + // Define el prompt para pedir ideas creativas sobre el tema + const prompt = ` + Por favor, genera una lista de ideas creativas sobre el siguiente tema. + Las ideas deben ser únicas y variadas, apropiadas para el contexto y el tema proporcionado. + + Tema: + + ${topic} + `; + + try { + const response = await axios.post( + url, + { + contents: [{ parts: [{ text: prompt }] }] + }, + { + headers: { 'Content-Type': 'application/json' } + } + ); + + // Extrae las ideas generadas de la respuesta + const ideas = response.data.candidates[0].content.parts[0].text; + return ideas; + } catch (error) { + console.error('Error al generar ideas:', JSON.stringify(error.response?.data, null, 2)); + return 'Hubo un problema al generar ideas creativas.'; + } +}; + +module.exports = { + brainstormIdeas +}; diff --git a/plugins/email_draft.js b/plugins/email_draft.js new file mode 100644 index 0000000..2e73889 --- /dev/null +++ b/plugins/email_draft.js @@ -0,0 +1,49 @@ +const axios = require('axios'); +const dotenv = require('dotenv'); + +dotenv.config(); + +const apiKey = process.env.GOOGLE_GEMINI_API_KEY; + +/** + * Genera un borrador de correo electrónico en base a una descripción y un tono. + * @param {String} description - Descripción del contenido del correo. + * @param {String} tone - Tono del correo, 'formal' o 'informal'. + * @returns {Promise} - Borrador de correo electrónico. + */ +const draftEmail = async (description, tone = 'formal') => { + const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent?key=${apiKey}`; + + // Define el prompt para pedir un correo con el tono adecuado + const prompt = ` + Por favor, redacta un correo electrónico en tono ${tone} basado en la siguiente descripción. + Asegúrate de que sea claro y apropiado para el contexto. + + Descripción del correo: + + ${description} + `; + + try { + const response = await axios.post( + url, + { + contents: [{ parts: [{ text: prompt }] }] + }, + { + headers: { 'Content-Type': 'application/json' } + } + ); + + // Extrae el borrador de correo de la respuesta + const emailDraft = response.data.candidates[0].content.parts[0].text; + return emailDraft; + } catch (error) { + console.error('Error al generar el borrador de correo:', JSON.stringify(error.response?.data, null, 2)); + return 'Hubo un problema al generar el borrador de correo electrónico.'; + } +}; + +module.exports = { + draftEmail +}; diff --git a/plugins/formatter.js b/plugins/formatter.js new file mode 100644 index 0000000..a38207f --- /dev/null +++ b/plugins/formatter.js @@ -0,0 +1,49 @@ +const axios = require('axios'); +const dotenv = require('dotenv'); + +dotenv.config(); + +const apiKey = process.env.GOOGLE_GEMINI_API_KEY; + +/** + * Formatea y organiza un texto con buena indentación y explicaciones. + * @param {String} text - Texto a formatear y organizar. + * @returns {Promise} - Texto formateado y organizado. + */ +const formatText = async (text) => { + const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent?key=${apiKey}`; + + // Definimos un prompt específico para pedir una reformateación y organización del texto + const prompt = ` + Por favor, organiza y formatea el siguiente texto con buena indentación y estructura, + proporciona explicaciones claras y usa subtítulos si es necesario. + No modifiques el contenido, pero haz que sea fácil de leer y entender. + + Texto a organizar: + + ${text} + `; + + try { + const response = await axios.post( + url, + { + contents: [{ parts: [{ text: prompt }] }] + }, + { + headers: { 'Content-Type': 'application/json' } + } + ); + + // Extrae el texto organizado de la respuesta + const formattedText = response.data.candidates[0].content.parts[0].text; + return formattedText; + } catch (error) { + console.error('Error al formatear el texto:', JSON.stringify(error.response?.data, null, 2)); + return text; // Devuelve el texto original en caso de error + } +}; + +module.exports = { + formatText +}; diff --git a/plugins/translator.js b/plugins/translator.js index 30c575c..c9ea5de 100644 --- a/plugins/translator.js +++ b/plugins/translator.js @@ -13,9 +13,10 @@ const apiKey = process.env.GOOGLE_GEMINI_API_KEY; */ const translateText = async (text, targetLang = 'es') => { const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent?key=${apiKey}`; + console.log('Texto crudo sin traduccion ' + text); // 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}`; + const prompt = `Eres un experto en idiomas, tu tarea es traducir el siguiente texto al español:\n\n${text}`; try { const response = await axios.post( @@ -30,6 +31,9 @@ const translateText = async (text, targetLang = 'es') => { // Extrae el texto traducido de la respuesta const translatedText = response.data.candidates[0].content.parts[0].text; + + console.log('Texto traducido: ' + translatedText); + return translatedText; } catch (error) { console.error('Error al traducir el texto:', JSON.stringify(error.response?.data, null, 2)); diff --git a/services/cron_service.js b/services/cron_service.js index b74b413..e91aa9f 100644 --- a/services/cron_service.js +++ b/services/cron_service.js @@ -1,56 +1,29 @@ const cron = require('node-cron'); -const { sendReminder } = require('../tasks/send_reminder'); -const config = require('../config.json'); +const { sendStoicReminder } = require('../tasks/stoicism_reminder'); /** - * Convierte el intervalo en horas o minutos a minutos. - * @param {String} intervalo - Intervalo en el formato "2 horas" o "45 minutos". - * @returns {Number} - Intervalo en minutos. - */ -const getIntervalInMinutes = (intervalo) => { - const [amount, unit] = intervalo.split(' '); - return unit.includes('hora') ? parseInt(amount) * 60 : parseInt(amount); -}; - -/** - * Programa las tareas de recordatorio basadas en el horario y el intervalo. + * Inicializa las tareas de recordatorio programadas para citas de estoicismo. * @param {Object} client - Instancia del cliente de WhatsApp. */ const initializeCronJobs = (client) => { - const dailyRoutines = config.rutinas.diarias; - - dailyRoutines.forEach((routine) => { - const { actividad, detalles } = routine; - const { intervalo, horario, que, hora } = detalles; - - if (intervalo && horario) { - const intervalInMinutes = getIntervalInMinutes(intervalo); - const [startHour, startMinute] = horario.inicio.split(':').map(Number); - const [endHour, endMinute] = horario.fin.split(':').map(Number); - - cron.schedule(`*/${intervalInMinutes} * * * *`, () => { - const now = new Date(); - const isWithinTimeFrame = - now.getHours() >= startHour && - now.getHours() <= endHour && - (now.getHours() < endHour || now.getMinutes() <= endMinute); - - if (isWithinTimeFrame) { - sendReminder(client, actividad, que); - } - }); - } - else if (hora) { - const [hour, minute] = hora.split(':'); - cron.schedule(`${minute} ${hour} * * *`, () => { - sendReminder(client, actividad, que); - }); - } + // Enviar recordatorio al despertar (7:00 AM) + cron.schedule('0 7 * * *', () => { + sendStoicReminder(client); }); - console.log('⏰ Recordatorios programados según configuración en config.json'); + // Enviar recordatorio al mediodía (12:00 PM) + cron.schedule('0 12 * * *', () => { + sendStoicReminder(client); + }); + + // Enviar recordatorio antes de dormir (9:00 PM) + cron.schedule('0 21 * * *', () => { + sendStoicReminder(client); + }); + + console.log('⏰ Recordatorios de citas de estoicismo programados para las 7:00 AM, 12:00 PM y 9:00 PM.'); }; module.exports = { initializeCronJobs -}; \ No newline at end of file +}; diff --git a/tasks/stoicism_reminder.js b/tasks/stoicism_reminder.js new file mode 100644 index 0000000..e2a5560 --- /dev/null +++ b/tasks/stoicism_reminder.js @@ -0,0 +1,50 @@ +const axios = require('axios'); +const { translateText } = require('../plugins/translator'); +const dotenv = require('dotenv'); + +dotenv.config(); + +const AUTHORIZED_NUMBER = process.env.AUTHORIZED_NUMBER; + +/** + * Obtiene una cita de estoicismo de la API y la traduce al español. + * @returns {Promise} - Cita traducida al español. + */ +const getStoicQuote = async () => { + try { + const response = await axios.get('https://stoic.tekloon.net/stoic-quote'); + const { author, quote } = response.data.data; + + // Traducir la cita al español + const translatedQuote = await translateText(quote, 'es'); + + // Formato mejorado para WhatsApp + return `━━━━━━━━━━━━━━━\n` + + `🧘‍♂️ *REMINDER* 🧘‍♀️\n\n` + + `\n${translatedQuote}\n\n` + + `*- ${author}*\n` + + `━━━━━━━━━━━━━━━`; + } catch (error) { + console.error('Error al obtener la cita de estoicismo:', error); + return '❌ Hubo un problema al obtener la cita de estoicismo.'; + } +}; + +/** + * Envía una cita de estoicismo traducida al español al número autorizado. + * @param {Object} client - Instancia del cliente de WhatsApp. + */ +const sendStoicReminder = async (client) => { + const quoteMessage = await getStoicQuote(); + try { + await client.sendMessage(AUTHORIZED_NUMBER, quoteMessage); + console.log(`📤 Cita de estoicismo enviada a ${AUTHORIZED_NUMBER}`); + } catch (error) { + console.error('❌ Error al enviar la cita de estoicismo:', error); + } +}; + +module.exports = { + sendStoicReminder, + getStoicQuote +};