initial commit
This commit is contained in:
commit
a28952edcf
3
.env.example
Normal file
3
.env.example
Normal file
@ -0,0 +1,3 @@
|
||||
API_KEY=
|
||||
AUTHORIZED_NUMBER=502xxxxxxxx@c.us
|
||||
CHROME_PATH=
|
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
.env
|
||||
bot.log
|
||||
.wwebjs_auth
|
||||
.wwebjs_cache
|
||||
node_modules
|
||||
temp
|
||||
config.json
|
87
README.md
Normal file
87
README.md
Normal file
@ -0,0 +1,87 @@
|
||||
# BodegAI - Asistente Personal en WhatsApp
|
||||
|
||||
 <!-- 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
152
config.example.json
Normal 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
148
index.js
Normal 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
1756
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
21
package.json
Normal file
21
package.json
Normal 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"
|
||||
}
|
||||
}
|
31
services/weather_service.js
Normal file
31
services/weather_service.js
Normal 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
65
utils/weather_utils.js
Normal 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
|
||||
};
|
Loading…
Reference in New Issue
Block a user