improved many thing

This commit is contained in:
Leifer Mendez
2022-01-15 12:36:46 +01:00
parent 4727114dee
commit 1d3410ac91
19 changed files with 848 additions and 993 deletions

2
.env.example Normal file
View File

@@ -0,0 +1,2 @@
SAVE_MEDIA=true
PORT=3000

5
.gitignore vendored
View File

@@ -1,3 +1,6 @@
/node_modules
/node_modules/*
session.json
session.json
chats/*
!chats/.gitkeep
.env

1
.prettierrc.json Normal file
View File

@@ -0,0 +1 @@

11
CHANGELOG.md Normal file
View File

@@ -0,0 +1,11 @@
Lucas Aldeco Brescia
hace 2 semanas
Bueno, descubrí el motivo, lo dejo acá por si a alguien le sirve. Cuando alguien publica un estado, se "recibe" un mensaje desde status@broadcast, al responder a esa dirección, se publica un nuevo estado. Basta con validar no responder a esa dirección y problema resuelto.
#### Actualización 14 Ene 2022
- npm update
- add mysql
- add env
- update route with middleware
- fix send message to story

View File

@@ -5,6 +5,10 @@ Hola amigos este BOT se realizo en vivo en mi canal de Youtube si quieres ver co
🤖 Link video https://www.youtube.com/watch?v=A_Xu0OR_HkE
#### Actualización 14 Ene 2022
- npm update
- add mysql
#### Node
> Debes de tener instalado NODE si no sabes como instalarlo te dejo un video en el cual explico como instalar node
__https://www.youtube.com/watch?v=6741ceWzsKQ&list=PL_WGMLcL4jzVY1y-SutA3N_PCNCAG7Y46&index=2&t=50s Minuto 0:50__

316
app.js
View File

@@ -1,99 +1,78 @@
/**
* ⚡⚡⚡ DECLARAMOS LAS LIBRERIAS y CONSTANTES A USAR! ⚡⚡⚡
*/
require('dotenv').config()
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 { Client } = require('whatsapp-web.js');
const { middlewareClient } = require('./middleware/client')
const { connectionReady, connectionLost } = require('./controllers/connection')
const { saveMedia } = require('./controllers/save')
const { getMessages, responseMessages } = require('./controllers/flows')
const { sendMedia, sendMessage, lastTrigger } = require('./controllers/send')
const app = express();
app.use(express.urlencoded({ extended: true }))
app.use(express.json())
const port = process.env.PORT || 3000
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....')}`);
}
var client;
var sessionData;
/**
* 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
}
const listenMessage = () => client.on('message', async msg => {
const { from, body, hasMedia } = msg;
// Este bug lo reporto Lucas Aldeco Brescia para evitar que se publiquen estados
if(from === 'status@broadcast'){
return
}
message = body.toLowerCase();
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)
/**
* Guardamos el archivo multimedia que envia
*/
if (process.env.SAVE_MEDIA && hasMedia) {
const media = await msg.downloadMedia();
saveMedia(media);
}
})
/**
* Ver si viene de un paso anterior
* Aqui podemos ir agregando más pasos
* a tu gusto!
*/
const lastStep = await lastTrigger(from) || null;
if (lastStep === 'STEP_2') {
const response = responseMessages(lastStep)
sendMessage(client, from, response);
return
}
if (lastStep === 'STEP_3') {
const response = responseMessages(lastStep)
sendMessage(client, from, response);
return
}
/**
* Respondemos al primero paso si encuentra palabras clave
*/
if (getMessages('STEP_1').includes(message)) {
const response = responseMessages('STEP_1')
sendMessage(client, from, response, 'STEP_2');
return
}
if (getMessages('STEP_2').includes(message)) {
const response = responseMessages('STEP_2')
sendMessage(client, from, response);
return
}
});
/**
* Revisamos si tenemos credenciales guardadas para inciar sessio
@@ -101,31 +80,19 @@ const replyAsk = (from, answer) => new Promise((resolve, reject) => {
*/
const withSession = () => {
// Si exsite cargamos el archivo con las credenciales
const spinner = ora(`Cargando ${chalk.yellow('Validando session con Whatsapp...')}`);
console.log(`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();
listenMessage()
loadRoutes(client);
});
client.on('auth_failure', () => {
spinner.stop();
console.log('** Error de autentificacion vuelve a generar el QRCODE (Borrar el archivo session.json) **');
})
client.on('auth_failure', () => connectionLost())
client.initialize();
}
@@ -135,27 +102,24 @@ const withSession = () => {
*/
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();
connectionReady()
});
client.on('auth_failure', () => {
console.log('** Error de autentificacion vuelve a generar el QRCODE **');
})
client.on('auth_failure', () => connectionLost());
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);
console.log(`Ocurrio un error con el archivo: `, err);
}
});
});
@@ -163,145 +127,19 @@ const withOutSession = () => {
client.initialize();
}
const connectionReady = () => {
listenMessage();
readExcel();
/**
* Cargamos rutas de express
*/
const loadRoutes = (client) => {
app.use('/api/', middlewareClient(client), require('./routes/api'))
}
/**
* 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!');
app.listen(port, () => {
console.log(`El server esta listo por el puerto ${port}`);
})

0
chats/.gitkeep Normal file
View File

Binary file not shown.

12
controllers/connection.js Normal file
View File

@@ -0,0 +1,12 @@
const connectionReady = (cb = () =>{}) => {
console.log('Client is ready!');
cb()
}
const connectionLost = (cb = () =>{}) => {
console.log('** Error de autentificacion vuelve a generar el QRCODE (Borrar el archivo session.json) **');
cb()
}
module.exports = {connectionReady, connectionLost}

26
controllers/flows.js Normal file
View File

@@ -0,0 +1,26 @@
const getMessages = (step) => {
switch (step) {
case 'STEP_1':
return ['hola', 'hi']
break;
case 'STEP_2':
return ['hola', 'hi']
break;
}
return null
}
const responseMessages = (step) => {
switch (step) {
case 'STEP_1':
return ['Si como estas', '🤔'].join('')
break;
case 'STEP_2':
return ['pa como estas', '🤔'].join('')
break;
}
return null
}
module.exports = { getMessages, responseMessages }

7
controllers/handle.js Normal file
View File

@@ -0,0 +1,7 @@
const cleanNumber = (number) => {
number = number.replace('@c.us', '');
number = `${number}@c.us`;
return number
}
module.exports = {cleanNumber}

17
controllers/save.js Normal file
View File

@@ -0,0 +1,17 @@
const mimeDb = require('mime-db')
/**
* Guardamos archivos multimedia que nuestro cliente nos envie!
* @param {*} media
*/
const saveMedia = () => {
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 **');
});
}
module.exports = {saveMedia}

102
controllers/send.js Normal file
View File

@@ -0,0 +1,102 @@
const ExcelJS = require('exceljs');
const moment = require('moment');
const fs = require('fs');
const { MessageMedia } = require('whatsapp-web.js');
const { cleanNumber } = require('./handle')
/**
* Enviamos archivos multimedia a nuestro cliente
* @param {*} number
* @param {*} fileName
*/
const sendMedia = (client, number, fileName) => {
number = cleanNumber(number)
const media = MessageMedia.fromFilePath(`${__dirname}/../mediaSend/${fileName}`);
client.sendMessage(number, media);
}
/**
* Enviamos un mensaje simple (texto) a nuestro cliente
* @param {*} number
*/
const sendMessage = (client, number = null, text = null, trigger = null) => {
number = cleanNumber(number)
const message = text
client.sendMessage(number, message);
readChat(number, message, trigger)
console.log(`⚡⚡⚡ Enviando mensajes....`);
}
/**
* Opte
*/
const lastTrigger = (number) => new Promise((resolve, reject) => {
number = cleanNumber(number)
const pathExcel = `${__dirname}/../chats/${number}.xlsx`;
const workbook = new ExcelJS.Workbook();
if(fs.existsSync(pathExcel))
{
workbook.xlsx.readFile(pathExcel)
.then(() => {
const worksheet = workbook.getWorksheet(1);
const lastRow = worksheet.lastRow;
const getRowPrevStep = worksheet.getRow(lastRow.number);
const lastStep = getRowPrevStep.getCell('C').value;
resolve(lastStep)
});
}else{
resolve(null)
}
})
/**
* Guardar historial de conversacion
* @param {*} number
* @param {*} message
*/
const readChat = async (number, message, trigger = null) => {
const pathExcel = `${__dirname}/../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.getCell('C').value = trigger;
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' },
{ header: 'Disparador', key: 'trigger' }
];
worksheet.addRow([today, message, trigger]);
workbook.xlsx.writeFile(pathExcel)
.then(() => {
console.log("saved");
})
.catch((err) => {
console.log("err", err);
});
}
}
module.exports = { sendMessage, sendMedia, lastTrigger }

10
controllers/web.js Normal file
View File

@@ -0,0 +1,10 @@
const { sendMessage } = require('../controllers/send')
const sendMessagePost = (req, res) => {
const { message, number } = req.body
const client = req.clientWs || null;
sendMessage(client, number, message)
res.send({ status: 'Enviado!' })
}
module.exports = { sendMessagePost }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

21
middleware/client.js Normal file
View File

@@ -0,0 +1,21 @@
const middlewareClient = (client = null) => async (req, res, next) => {
try {
if(!client){
res.status(409)
console.log(client)
res.send({ error: 'Error de client.' })
}else{
req.clientWs = client;
next()
}
} catch (e) {
console.log(e)
res.status(409)
res.send({ error: 'Error de client' })
}
}
module.exports = { middlewareClient }

1281
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -4,22 +4,25 @@
"description": "",
"main": "index.js",
"scripts": {
"start": "node app.js",
"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",
"dotenv": "^11.0.0",
"exceljs": "^4.3.0",
"express": "^4.17.2",
"file-type": "^16.5.3",
"mime-db": "^1.51.0",
"moment": "^2.29.1",
"ora": "^5.4.0",
"qrcode-terminal": "^0.12.0",
"whatsapp-web.js": "^1.12.5",
"socket.io": "^4.4.1",
"whatsapp-web.js": "^1.15.3",
"xlsx": "^0.16.9"
},
"devDependencies": {
"prettier": "2.5.1"
}
}

7
routes/api.js Normal file
View File

@@ -0,0 +1,7 @@
const express = require('express')
const router = express.Router();
const { sendMessagePost } = require('../controllers/web')
router.post('/send', sendMessagePost)
module.exports = router