mirror of
https://github.com/cheveguerra/bot-whatsapp.git
synced 2026-04-20 20:49:15 +00:00
Init
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
/node_modules
|
||||||
|
/node_modules/*
|
||||||
307
app.js
Normal file
307
app.js
Normal file
@@ -0,0 +1,307 @@
|
|||||||
|
/**
|
||||||
|
* ⚡⚡⚡ DECLARAMOS LAS LIBRERIAS y CONSTANTES A USAR! ⚡⚡⚡
|
||||||
|
*/
|
||||||
|
const fs = require('fs');
|
||||||
|
const mimeDb = require('mime-db')
|
||||||
|
const express = require('express');
|
||||||
|
const moment = require('moment');
|
||||||
|
const ora = require('ora');
|
||||||
|
const chalk = require('chalk');
|
||||||
|
const ExcelJS = require('exceljs');
|
||||||
|
const qrcode = require('qrcode-terminal');
|
||||||
|
const { flowConversation } = require('./conversation')
|
||||||
|
const { Client, MessageMedia } = require('whatsapp-web.js');
|
||||||
|
const app = express();
|
||||||
|
app.use(express.urlencoded({ extended: true }))
|
||||||
|
const SESSION_FILE_PATH = './session.json';
|
||||||
|
let client;
|
||||||
|
let sessionData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Guardamos archivos multimedia que nuestro cliente nos envie!
|
||||||
|
* @param {*} media
|
||||||
|
*/
|
||||||
|
const saveMedia = (media) => {
|
||||||
|
|
||||||
|
const extensionProcess = mimeDb[media.mimetype]
|
||||||
|
const ext = extensionProcess.extensions[0]
|
||||||
|
fs.writeFile(`./media/${media.filename}.${ext}`, media.data, { encoding: 'base64' }, function (err) {
|
||||||
|
console.log('** Archivo Media Guardado **');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enviamos archivos multimedia a nuestro cliente
|
||||||
|
* @param {*} number
|
||||||
|
* @param {*} fileName
|
||||||
|
*/
|
||||||
|
const sendMedia = (number, fileName) => {
|
||||||
|
number = number.replace('@c.us', '');
|
||||||
|
number = `${number}@c.us`
|
||||||
|
const media = MessageMedia.fromFilePath(`./mediaSend/${fileName}`);
|
||||||
|
client.sendMessage(number, media);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enviamos un mensaje simple (texto) a nuestro cliente
|
||||||
|
* @param {*} number
|
||||||
|
*/
|
||||||
|
const sendMessage = (number = null, text = null) => {
|
||||||
|
number = number.replace('@c.us', '');
|
||||||
|
number = `${number}@c.us`
|
||||||
|
const message = text || `Hola soy un BOT recuerda https://www.youtube.com/leifermendez`;
|
||||||
|
client.sendMessage(number, message);
|
||||||
|
readChat(number, message)
|
||||||
|
console.log(`${chalk.red('⚡⚡⚡ Enviando mensajes....')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escuchamos cuando entre un mensaje
|
||||||
|
*/
|
||||||
|
const listenMessage = () => {
|
||||||
|
client.on('message', async msg => {
|
||||||
|
const { from, to, body } = msg;
|
||||||
|
//34691015468@c.us
|
||||||
|
console.log(msg.hasMedia);
|
||||||
|
if (msg.hasMedia) {
|
||||||
|
const media = await msg.downloadMedia();
|
||||||
|
saveMedia(media);
|
||||||
|
// do something with the media data here
|
||||||
|
}
|
||||||
|
|
||||||
|
await greetCustomer(from);
|
||||||
|
|
||||||
|
console.log(body);
|
||||||
|
|
||||||
|
await replyAsk(from, body);
|
||||||
|
|
||||||
|
// await readChat(from, body)
|
||||||
|
// console.log(`${chalk.red('⚡⚡⚡ Enviando mensajes....')}`);
|
||||||
|
// console.log('Guardar este número en tu Base de Datos:', from);
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response a pregunta
|
||||||
|
*/
|
||||||
|
|
||||||
|
const replyAsk = (from, answer) => new Promise((resolve, reject) => {
|
||||||
|
console.log(`---------->`, answer);
|
||||||
|
if (answer === 'Quieromeme') {
|
||||||
|
sendMedia(from, 'meme-1.png')
|
||||||
|
resolve(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Revisamos si tenemos credenciales guardadas para inciar sessio
|
||||||
|
* este paso evita volver a escanear el QRCODE
|
||||||
|
*/
|
||||||
|
const withSession = () => {
|
||||||
|
// Si exsite cargamos el archivo con las credenciales
|
||||||
|
const spinner = ora(`Cargando ${chalk.yellow('Validando session con Whatsapp...')}`);
|
||||||
|
sessionData = require(SESSION_FILE_PATH);
|
||||||
|
spinner.start();
|
||||||
|
client = new Client({
|
||||||
|
session: sessionData
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('ready', () => {
|
||||||
|
console.log('Client is ready!');
|
||||||
|
spinner.stop();
|
||||||
|
|
||||||
|
// sendMessage();
|
||||||
|
// sendMedia();
|
||||||
|
|
||||||
|
connectionReady();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
client.on('auth_failure', () => {
|
||||||
|
spinner.stop();
|
||||||
|
console.log('** Error de autentificacion vuelve a generar el QRCODE (Borrar el archivo session.json) **');
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
client.initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generamos un QRCODE para iniciar sesion
|
||||||
|
*/
|
||||||
|
const withOutSession = () => {
|
||||||
|
console.log('No tenemos session guardada');
|
||||||
|
client = new Client();
|
||||||
|
client.on('qr', qr => {
|
||||||
|
qrcode.generate(qr, { small: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('ready', () => {
|
||||||
|
console.log('Client is ready!');
|
||||||
|
connectionReady();
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('auth_failure', () => {
|
||||||
|
console.log('** Error de autentificacion vuelve a generar el QRCODE **');
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
client.on('authenticated', (session) => {
|
||||||
|
// Guardamos credenciales de de session para usar luego
|
||||||
|
sessionData = session;
|
||||||
|
fs.writeFile(SESSION_FILE_PATH, JSON.stringify(session), function (err) {
|
||||||
|
if (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
client.initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
const connectionReady = () => {
|
||||||
|
listenMessage();
|
||||||
|
readExcel();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Difundir mensaje a clientes
|
||||||
|
*/
|
||||||
|
const readExcel = async () => {
|
||||||
|
const pathExcel = `./chats/clientes-saludar.xlsx`;
|
||||||
|
const workbook = new ExcelJS.Workbook();
|
||||||
|
await workbook.xlsx.readFile(pathExcel);
|
||||||
|
const worksheet = workbook.getWorksheet(1);
|
||||||
|
const columnNumbers = worksheet.getColumn('A');
|
||||||
|
columnNumbers.eachCell((cell, rowNumber) => {
|
||||||
|
const numberCustomer = cell.value
|
||||||
|
|
||||||
|
const columnDate = worksheet.getRow(rowNumber);
|
||||||
|
let prevDate = columnDate.getCell(2).value;
|
||||||
|
prevDate = moment.unix(prevDate);
|
||||||
|
const diffMinutes = moment().diff(prevDate, 'minutes');
|
||||||
|
|
||||||
|
// Si ha pasado mas de 60 minuitos podemos enviar nuevamente
|
||||||
|
if (diffMinutes > 60) {
|
||||||
|
sendMessage(numberCustomer)
|
||||||
|
columnDate.getCell(2).value = moment().format('X')
|
||||||
|
columnDate.commit();
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
workbook.xlsx.writeFile(pathExcel);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Guardar historial de conversacion
|
||||||
|
* @param {*} number
|
||||||
|
* @param {*} message
|
||||||
|
*/
|
||||||
|
const readChat = async (number, message) => {
|
||||||
|
const pathExcel = `./chats/${number}.xlsx`;
|
||||||
|
const workbook = new ExcelJS.Workbook();
|
||||||
|
const today = moment().format('DD-MM-YYYY hh:mm')
|
||||||
|
|
||||||
|
if (fs.existsSync(pathExcel)) {
|
||||||
|
/**
|
||||||
|
* Si existe el archivo de conversacion lo actualizamos
|
||||||
|
*/
|
||||||
|
const workbook = new ExcelJS.Workbook();
|
||||||
|
workbook.xlsx.readFile(pathExcel)
|
||||||
|
.then(() => {
|
||||||
|
const worksheet = workbook.getWorksheet(1);
|
||||||
|
const lastRow = worksheet.lastRow;
|
||||||
|
var getRowInsert = worksheet.getRow(++(lastRow.number));
|
||||||
|
getRowInsert.getCell('A').value = today;
|
||||||
|
getRowInsert.getCell('B').value = message;
|
||||||
|
getRowInsert.commit();
|
||||||
|
workbook.xlsx.writeFile(pathExcel);
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
/**
|
||||||
|
* NO existe el archivo de conversacion lo creamos
|
||||||
|
*/
|
||||||
|
const worksheet = workbook.addWorksheet('Chats');
|
||||||
|
worksheet.columns = [
|
||||||
|
{ header: 'Fecha', key: 'number_customer' },
|
||||||
|
{ header: 'Mensajes', key: 'message' }
|
||||||
|
];
|
||||||
|
worksheet.addRow([today, message]);
|
||||||
|
workbook.xlsx.writeFile(pathExcel)
|
||||||
|
.then(() => {
|
||||||
|
|
||||||
|
console.log("saved");
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log("err", err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saludos a primera respuesta
|
||||||
|
* @param {*} req
|
||||||
|
* @param {*} res
|
||||||
|
*/
|
||||||
|
|
||||||
|
const greetCustomer = (from) => new Promise((resolve, reject) => {
|
||||||
|
from = from.replace('@c.us', '');
|
||||||
|
|
||||||
|
const pathExcel = `./chats/${from}@c.us.xlsx`;
|
||||||
|
if (!fs.existsSync(pathExcel)) {
|
||||||
|
const firstMessage = [
|
||||||
|
'👋 Ey! que pasa bro',
|
||||||
|
'Recuerda subscribirte a mi canal de YT',
|
||||||
|
'https://www.youtube.com/leifermendez',
|
||||||
|
'de regalo te dejo algunos de mis cursos',
|
||||||
|
'🔴 Aprende ANGULAR desde cero 2021 ⮕ https://bit.ly/367tJ32',
|
||||||
|
'✅ Aprende NODE desde cero 2021 ⮕ https://bit.ly/3od1Bl6',
|
||||||
|
'🔵 (Socket.io) NODE (Tutorial) ⮕ https://bit.ly/3pg1Q02',
|
||||||
|
'------',
|
||||||
|
'------',
|
||||||
|
'Veo que es la primera vez que nos escribes ¿Quieres que te envie un MEME?',
|
||||||
|
'Responde Quieromeme'
|
||||||
|
].join(' ')
|
||||||
|
|
||||||
|
sendMessage(from, firstMessage)
|
||||||
|
sendMedia(from, 'curso-1-1.png')
|
||||||
|
sendMedia(from, 'curso-2.png')
|
||||||
|
sendMedia(from, 'curso-3.png')
|
||||||
|
}
|
||||||
|
resolve(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controladores
|
||||||
|
*/
|
||||||
|
|
||||||
|
const sendMessagePost = (req, res) => {
|
||||||
|
const { message, number } = req.body
|
||||||
|
console.log(message, number);
|
||||||
|
sendMessage(number, message)
|
||||||
|
res.send({ status: 'Enviado!' })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rutas
|
||||||
|
*/
|
||||||
|
|
||||||
|
app.post('/send', sendMessagePost);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Revisamos si existe archivo con credenciales!
|
||||||
|
*/
|
||||||
|
(fs.existsSync(SESSION_FILE_PATH)) ? withSession() : withOutSession();
|
||||||
|
|
||||||
|
|
||||||
|
app.listen(9000, () => {
|
||||||
|
console.log('Server ready!');
|
||||||
|
})
|
||||||
BIN
chats/34691015468@c.us.xlsx
Normal file
BIN
chats/34691015468@c.us.xlsx
Normal file
Binary file not shown.
BIN
chats/clientes-saludar.xlsx
Normal file
BIN
chats/clientes-saludar.xlsx
Normal file
Binary file not shown.
3
conversation.js
Normal file
3
conversation.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
const flowConversation = () => { }
|
||||||
|
|
||||||
|
module.exports = { flowConversation }
|
||||||
BIN
media/undefined
Normal file
BIN
media/undefined
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
BIN
media/undefined.jpeg
Normal file
BIN
media/undefined.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
BIN
media/undefined.mp4
Normal file
BIN
media/undefined.mp4
Normal file
Binary file not shown.
BIN
mediaSend/curso-1-1.png
Normal file
BIN
mediaSend/curso-1-1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 322 KiB |
BIN
mediaSend/curso-2.png
Normal file
BIN
mediaSend/curso-2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 307 KiB |
BIN
mediaSend/curso-3.png
Normal file
BIN
mediaSend/curso-3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 278 KiB |
BIN
mediaSend/meme-1.png
Normal file
BIN
mediaSend/meme-1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 447 KiB |
2257
package-lock.json
generated
Normal file
2257
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
25
package.json
Normal file
25
package.json
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"name": "test-ws-bot",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"chalk": "^4.1.0",
|
||||||
|
"excel4node": "^1.7.2",
|
||||||
|
"exceljs": "^4.2.1",
|
||||||
|
"express": "^4.17.1",
|
||||||
|
"file-type": "^16.3.0",
|
||||||
|
"mime-db": "^1.46.0",
|
||||||
|
"moment": "^2.29.1",
|
||||||
|
"ora": "^5.4.0",
|
||||||
|
"qrcode-terminal": "^0.12.0",
|
||||||
|
"whatsapp-web.js": "^1.12.5",
|
||||||
|
"xlsx": "^0.16.9"
|
||||||
|
}
|
||||||
|
}
|
||||||
1
session.json
Normal file
1
session.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"WABrowserId":"\"98X+ryPCw6NGBS5XUzY3uQ==\"","WASecretBundle":"{\"key\":\"sNHCjpK4m8HRo/UVSLxZSrkfV2XRl0cCeQjuAjXifWQ=\",\"encKey\":\"8U/QYE2oYRlogHVUvVkasP4VxdqI1A7F8Ncs2tHKTwk=\",\"macKey\":\"sNHCjpK4m8HRo/UVSLxZSrkfV2XRl0cCeQjuAjXifWQ=\"}","WAToken1":"\"R1GVXpZG3tTsedcWMmy5d5G6J7ZqWzCeZpKjxU+RbI4=\"","WAToken2":"\"1@MW/xvqV32Wy0SnGbcGYApbZWUY4zsB1G3Hhl+P4eCPa5IEOZTJfMXjAJRy7K03bNfPEkErz/xR09Xw==\""}
|
||||||
Reference in New Issue
Block a user