initial commit

This commit is contained in:
David Vargas 2024-11-05 21:28:29 -06:00
commit a28952edcf
9 changed files with 2270 additions and 0 deletions

3
.env.example Normal file
View File

@ -0,0 +1,3 @@
API_KEY=
AUTHORIZED_NUMBER=502xxxxxxxx@c.us
CHROME_PATH=

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
.env
bot.log
.wwebjs_auth
.wwebjs_cache
node_modules
temp
config.json

87
README.md Normal file
View File

@ -0,0 +1,87 @@
# BodegAI - Asistente Personal en WhatsApp
![BodegAI Logo](path_to_your_logo.png) <!-- Reemplaza con la ruta a tu logo si tienes uno -->
BodegAI es un asistente personal desarrollado como un bot para WhatsApp, diseñado para ayudarte a gestionar y organizar aspectos clave de tu vida diaria. Ideal para profesionales como programadores que buscan mantener un equilibrio entre el trabajo y el bienestar personal.
## Tabla de Contenidos
- [BodegAI - Asistente Personal en WhatsApp](#bodegai---asistente-personal-en-whatsapp)
- [Tabla de Contenidos](#tabla-de-contenidos)
- [Características](#características)
- [Requisitos](#requisitos)
- [Instalación](#instalación)
- [Uso](#uso)
- [Comandos Disponibles](#comandos-disponibles)
## Características
- **Notificaciones Personalizadas**: Recibe recordatorios para beber agua, limpiar tu espacio, cocinar y más.
- **Información del Clima**: Consulta el clima actual de cualquier ciudad directamente desde WhatsApp.
- **Gestión de Finanzas**: Mantén un seguimiento de tus ingresos y gastos.
- **Mantenimiento del Hogar**: Controla el sistema de agua y otros servicios esenciales.
- **Planificación de Compras**: Organiza tus visitas a la tienda y gestiona tu lista de despensa.
## Requisitos
- **Node.js**: Versión 14 o superior.
- **npm**: Versión 6 o superior.
- **WhatsApp Web**: Una cuenta de WhatsApp para conectar el bot.
- **Moto**: Para desplazarte a la tienda en tu ubicación actual.
## Instalación
1. **Clona el repositorio:**
```bash
git clone https://git.davidwebgt.com/davidwebgt/bodegAI.git
cd bodegAI
```
2. **Instala las dependencias:**
```bash
npm install
```
3. **Configura las variables de entorno:**
Crea un archivo `.env` en la raíz del proyecto y añade las siguientes variables:
```env
CHROME_PATH=/ruta/a/tu/instalación/de/chrome
AUTHORIZED_NUMBER=+1234567890
```
- `CHROME_PATH`: Ruta al ejecutable de Chrome en tu sistema.
- `AUTHORIZED_NUMBER`: Número de teléfono autorizado para interactuar con el bot.
## Uso
1. **Inicia el bot:**
```bash
node index.js
```
2. **Escanea el QR Code:**
Al iniciar, el bot generará un código QR en la terminal. Escanéalo con tu aplicación de WhatsApp para conectar el bot a tu cuenta.
3. **Interactúa con el Bot:**
Envía comandos desde tu WhatsApp para recibir respuestas y gestionar tus tareas.
## Comandos Disponibles
- **!ping**
- **Descripción**: Verifica que el bot está activo.
- **Uso**: Envía `!ping` y el bot responderá con `pong`.
- **!clima [ciudad]**
- **Descripción**: Obtiene la información del clima actual para la ciudad especificada. Si no se proporciona una ciudad, usa "Guatemala" por defecto.
- **Uso**:
- `!clima`
- `!clima Ciudad de México`

152
config.example.json Normal file
View File

@ -0,0 +1,152 @@
{
"perfil": {
"personal": {
"nombre": {
"completo": "",
"preferido": ""
},
"contacto": {
"telefono": {
"codigo_pais": "",
"numero": ""
},
"email": ""
},
"ubicacion": {
"tipo": "",
"direccion": "",
"referencia": "",
"movilidad": {
"tipo": "",
"uso": []
}
}
},
"profesional": {
"puesto": "",
"modalidad": {
"tipo": "",
"distribucion": {
"ciclo_presencial": 0,
"ciclo_remoto": 0,
"frecuencia": ""
}
},
"compensacion": {
"salario_base": 0,
"fechas_pago": [
{
"dia": 0,
"descripcion": ""
}
]
}
}
},
"finanzas": {
"ingresos": {
"fijos": {
"salario": 0
},
"total_mensual": 0
},
"egresos": {
"fijos": {
"servicios": {
"internet": 0,
"telefono": 0
},
"obligaciones": {
"universidad": 0,
"prestamo": 0
}
},
"variables": {
"servicios": {
"electricidad": 0,
"gas": 0
},
"necesidades": {
"alimentacion": 0
}
},
"total_fijos": 0,
"total_variables_estimado": 0
}
},
"hogar": {
"servicios": {
"agua": {
"suministro": {
"tipo": "",
"sistema": "",
"operacion": ""
},
"mantenimiento": {
"actividades": [],
"frecuencia": ""
}
}
},
"administracion": {
"cocina": {
"abastecimiento": {
"frecuencia": 0,
"actividades": []
}
},
"compras": {
"frecuencia": "",
"categorias": [],
"logistica": {
"transporte": "",
"observaciones": ""
}
}
}
},
"rutinas": {
"diarias": [
{
"actividad": "",
"detalles": {
"que": "",
"intervalo": "",
"horario": {
"inicio": "",
"fin": ""
}
}
}
],
"semanales": [
{
"actividad": "",
"detalles": {
"frecuencia": "",
"dias_preferidos": []
}
}
]
},
"objetivos": {
"personales": {
"salud": {
"meta": "",
"acciones": []
},
"balance": {
"meta": "",
"acciones": []
}
},
"domesticos": {
"meta": "",
"acciones": []
},
"economicos": {
"meta": "",
"acciones": []
}
}
}

148
index.js Normal file
View File

@ -0,0 +1,148 @@
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');
// Configuración inicial
dotenv.config();
// Constantes
const CHROME_PATH = process.env.CHROME_PATH;
const AUTHORIZED_NUMBER = process.env.AUTHORIZED_NUMBER;
// Validación inicial
if (!AUTHORIZED_NUMBER) {
console.error('❌ Debes establecer el número autorizado en el archivo .env');
process.exit(1);
}
// Configuración del cliente
const clientConfig = {
authStrategy: new LocalAuth(),
puppeteer: {
headless: true,
executablePath: CHROME_PATH,
args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage']
}
};
class WhatsAppBot {
constructor() {
this.client = new Client(clientConfig);
this.myId = null;
this.myNumber = null;
this.initializeEventHandlers();
}
initializeEventHandlers() {
this.client.on('qr', this.handleQR);
this.client.on('ready', this.handleReady.bind(this));
this.client.on('message_create', this.handleMessage.bind(this));
this.client.on('message', this.logMessage);
this.client.on('group_join', this.handleGroupJoin);
}
handleQR(qr) {
qrcode.generate(qr, { small: true });
console.log('🔍 Escanea el QR code con tu WhatsApp.');
}
async handleReady() {
console.log('✅ 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}`);
const chat = await this.client.getChatById(this.myId);
console.log('💭 Chat propio encontrado:', chat);
} catch (error) {
console.error(`❌ Error al obtener información: ${error}`);
}
}
isChatAllowed(messageFrom) {
const isAllowed = messageFrom === AUTHORIZED_NUMBER;
if (isAllowed) console.log('🎯 Mensaje detectado desde el número autorizado');
return isAllowed;
}
async handleMessage(message) {
if (!this.myId || !this.isChatAllowed(message.from)) return;
console.log('📨 Mensaje creado:', {
from: message.from,
fromMe: message.fromMe,
body: message.body,
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 this.handleWeatherCommand(message);
}
}
async handlePingCommand(message) {
try {
await this.client.sendMessage(message.from, 'pong');
console.log(`📤 Respondido 'pong' al número autorizado ${message.from}`);
} catch (error) {
console.error('❌ Error al enviar respuesta:', error);
}
}
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,
fromMe: message.fromMe,
body: message.body
});
}
handleGroupJoin(notification) {
console.log('👥 Bot añadido a un nuevo grupo:', notification.id.remote);
}
start() {
this.client.initialize();
}
}
// Iniciar el bot
const bot = new WhatsAppBot();
bot.start();

1756
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

21
package.json Normal file
View File

@ -0,0 +1,21 @@
{
"name": "whatsapp_bot",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"axios": "^1.7.7",
"dotenv": "^16.4.5",
"form-data": "^4.0.1",
"node-cron": "^3.0.3",
"qrcode-terminal": "^0.12.0",
"whatsapp-web.js": "^1.26.0",
"winston": "^3.15.0"
}
}

View File

@ -0,0 +1,31 @@
const axios = require('axios');
const getWeather = async (latitude, longitude) => {
try {
const response = await axios.get('https://api.open-meteo.com/v1/forecast', {
params: {
latitude: latitude,
longitude: longitude,
current_weather: true,
timezone: 'auto'
}
});
if (!response.data.current_weather) {
throw new Error('Datos de clima no disponibles.');
}
const weather = response.data.current_weather;
return {
temperature: weather.temperature,
windspeed: weather.windspeed,
winddirection: weather.winddirection,
weathercode: weather.weathercode,
time: weather.time
};
} catch (error) {
throw new Error(`Error al obtener el clima: ${error.message}`);
}
};
module.exports = { getWeather };

65
utils/weather_utils.js Normal file
View File

@ -0,0 +1,65 @@
const axios = require('axios');
const getCoordinates = async (city) => {
try {
const response = await axios.get('https://nominatim.openstreetmap.org/search', {
params: {
q: city,
format: 'json',
limit: 1
},
headers: {
'User-Agent': 'WhatsApp-Bot/1.0 (josuedavidvl18@gmail.com)' // Reemplaza con tu email
}
});
if (response.data.length === 0) {
throw new Error('Ciudad no encontrada.');
}
const { lat, lon } = response.data[0];
return { latitude: lat, longitude: lon };
} catch (error) {
throw new Error(`Error al obtener coordenadas: ${error.message}`);
}
};
const translateWeatherCode = (code) => {
const weatherCodes = {
0: 'Cielo despejado',
1: 'Parcialmente nublado',
2: 'Nublado',
3: 'Cubierto',
45: 'Niebla',
48: 'Depositos de escarcha',
51: 'Llovizna ligera',
53: 'Llovizna moderada',
55: 'Llovizna densa',
56: 'Llovizna helada ligera',
57: 'Llovizna helada densa',
61: 'Lluvia ligera',
63: 'Lluvia moderada',
65: 'Lluvia fuerte',
66: 'Lluvia helada ligera',
67: 'Lluvia helada fuerte',
71: 'Nevada ligera',
73: 'Nevada moderada',
75: 'Nevada fuerte',
77: 'Aguanieve',
80: 'Chubascos ligeros',
81: 'Chubascos moderados',
82: 'Chubascos intensos',
85: 'Aguanieve ligera',
86: 'Aguanieve fuerte',
95: 'Tormenta',
96: 'Tormenta con granizo ligero',
99: 'Tormenta con granizo fuerte'
};
return weatherCodes[code] || 'Desconocido';
};
module.exports = {
getCoordinates,
translateWeatherCode
};