Untitled
unknown
javascript
a year ago
23 kB
4
Indexable
Never
const fs = require("fs"); const XLSX = require("xlsx"); const moment = require("moment"); const { google } = require("googleapis"); const { Email } = require("../utils/email"); const { AppError } = require("../utils/appError"); const { MessageMedia } = require("whatsapp-web.js"); const { createLog } = require("../utils/createLog"); const { allSessionsObject } = require("../client/client"); const { sendResponses } = require("../utils/sendResponses"); const { phoneNumberFormatter } = require("../utils/formatter"); const { codStatus, codStatusHttp } = require("../utils/dictionaries"); const { indexRepository } = require("../repositories/index.repository"); const { greetings, dates, emojis, farewells } = require("../utils/whatsappMiscellany"); const today = new Date(); today.setHours(0, 0, 0, 0); // * Set the time to 00:00:00:0000 moment.locale("es"); const maxCharMessages = 180; class WhastappsServices { static async controlSubscription(countryCode, phone, clientId) { try { const { whatsappRepository } = indexRepository(); const findSuscription = await whatsappRepository.findSuscriptionByCountryCodeClientAndPhone( countryCode, phone, clientId ); if (findSuscription) { if (findSuscription.statusId.toString() === codStatus.canceled) { return false; } else { return true; } } else { await whatsappRepository.createSuscription( countryCode, phone, `${countryCode}${phone}`, clientId ); return true; } } catch (error) { throw error; } } static async randomCharacters(key) { try { const result = Math.floor(Math.random() * key.length); return result; } catch (error) { throw error; } } static async sendMessage(client, countryCode, number, message, file, currentUser, workspaces) { try { const { whatsappRepository } = indexRepository(); const phone = await phoneNumberFormatter(countryCode, number); const randomIndexGreeting = await WhastappsServices.randomCharacters(greetings); const randomIndexFarewells = await WhastappsServices.randomCharacters(farewells); const randomIndexEmojis = await WhastappsServices.randomCharacters(emojis); const randomIndexMessages = await WhastappsServices.randomCharacters(message); const randomIndexDates = await WhastappsServices.randomCharacters(dates); const newMessage = `${greetings[randomIndexGreeting]},\n${emojis[randomIndexEmojis]}${emojis[randomIndexEmojis]} ${message[randomIndexMessages]} ${emojis[randomIndexEmojis]}${emojis[randomIndexEmojis]}\n${farewells[randomIndexFarewells]}\n${dates[randomIndexDates]}\nSi desea desuscribirse, por favor escriba: *Desuscribir*`; const chat = await client.getChatById(phone); await chat.sendStateTyping(); const wpm = (newMessage.split(" ").length / 90) * 60000; // ^ 90 words per minute if (wpm < 25000) { // ^ 25 seconds because it is the default time of the sendStateTyping() await new Promise((r) => setTimeout(r, wpm)); } else { await new Promise((r) => setTimeout(r, 25000)); } let media; if (file) { const { mimetype, destination, filename } = file; const base64Document = fs.readFileSync(`${destination}${filename}`, { encoding: "base64" }); media = new MessageMedia(mimetype, base64Document, filename); } await chat .sendMessage(newMessage, { media }) .then(async () => { await chat.clearState(); await whatsappRepository.createStat( client.info.me.user, `(+${countryCode})${number}`, newMessage, false, // ! It's pending to do currentUser.id ); }) .catch(async (err) => { console.log(err); let messageDescription = `${workspaces} Ocurrió un error inesperado en el sistema: ${phone}`; await createLog(messageDescription, true, err, currentUser.id, codStatus.error); }); } catch (error) { throw error; } } static async excelToJson(file) { try { const workbook = await XLSX.readFile(file.path); const workbookSheets = workbook.SheetNames; const sheet = workbookSheets[0]; const dataExcel = await XLSX.utils.sheet_to_json(workbook.Sheets[sheet]); return dataExcel; } catch (error) { throw error; } } static async contactBook(countryCode, phone, clientId, workspaces) { try { let messageDescription; const { whatsappRepository } = indexRepository(); const googleCredentials = await whatsappRepository.findGoogleCredentialsByClientId(clientId); const auth = new google.auth.OAuth2( googleCredentials.googleClientId, googleCredentials.googleClientSecret, googleCredentials.googleClientUri ); auth.setCredentials({ refresh_token: googleCredentials.googleRefreshToken }); const service = google.people({ version: "v1", auth }); let phoneNumber = `${countryCode}${phone}`; await service.people.searchContacts( { query: phoneNumber, readMask: "phoneNumbers" }, async (err, res) => { if (err) { console.error("Error al buscar el número:", err); await new Email("inclub.dev@gmail.com") .contactBookError(JSON.stringify(googleCredentials), JSON.stringify(err)) .then(async () => { messageDescription = `${workspaces} Envio exitoso del correo para el desarrollador: inclub.dev@gmail.com`; await createLog(messageDescription, false, null, null, codStatus.active); }) .catch(async (err) => { messageDescription = `${workspaces} Ocurrio algo inesperado enviando el correo para el desarrollador: inclub.dev@gmail.com`; await createLog(messageDescription, true, err, null, codStatus.error); }); } if (res) { const contacts = await res.data.results; if (!contacts || contacts.length === 0) { phoneNumber = "+" + phoneNumber; const contact = { phoneNumbers: [{ value: phoneNumber }] }; await service.people.createContact({ requestBody: contact }, (err, res) => { if (err) { console.error("Error al crear el contacto:", err); } console.log("Contacto creado:", res.data); }); } else { console.log("Contacto encontrado:", contacts[0]); } } } ); } catch (error) { throw error; } } static async validatePhone(phoneClientQr, phoneClientDb, workspaces, currentUser, next) { try { if (phoneClientQr !== phoneClientDb) { let messageDescription = `${workspaces} El número registrado en DB debe ser el mismo al que escanea el QR -- QR: ${phoneClientQr} -- DB: ${phoneClientDb}`; await createLog(messageDescription, false, null, currentUser.id, codStatus.active); return next( new AppError( codStatusHttp.forbidden, "The registered number does not correspond to the one used in the QR" ) ); } } catch (error) { throw error; } } static async validateSize(file, workspaces, currentUser, next) { try { let size = file.size; if (size >= 1000000) { let messageDescription = `${workspaces} El documento cargado supera 1MB`; await createLog(messageDescription, true, null, currentUser.id, codStatus.error); return next( new AppError( codStatusHttp.badRequest, "You must provide a document with a weight of less than 1mb" ) ); } } catch (error) { throw error; } } static async validateLimitPerDay(user, currentUser, next) { try { const { whatsappRepository } = indexRepository(); const stats = await whatsappRepository.findAllStatsAndCount(user, today); if (stats.count >= 901) { let messageDescription = `${workspaces} El cliente con numero ${user} llego al limite de mensajes diarios (900)`; await createLog(messageDescription, true, null, currentUser.id, codStatus.error); return next( new AppError( codStatusHttp.manyRequest, "You exceeded the limit of conservations in the day" ) ); } } catch (error) { throw error; } } static async validateSuscription( client, countryCode, phone, arrayMessages, file, clientId, counterRow, i, currentUser, workspaces ) { try { let suscription = await WhastappsServices.controlSubscription(countryCode, phone, clientId); if (suscription) { await WhastappsServices.contactBook(countryCode, phone, currentUser.clientId, workspaces); // * This is the delay if (i % 10 === 0 && i !== 0) { await new Promise((resolve) => setTimeout(resolve, 100000)); // * 100 Seg } await WhastappsServices.sendMessage( client.client, Number(countryCode), `0${phone}`, arrayMessages, file, currentUser, workspaces ); counterRow += 1; } else { let messageDescription = `${workspaces} No se envio mensaje al numero (+${countryCode}) ${phone} - Motivo: Desuscrito`; await createLog(messageDescription, true, null, currentUser.id, codStatus.error); } } catch (error) { throw error; } } static async validateClient(req, res, next, workspaces) { try { const { id } = req.params; console.log(id) let messageDescription; messageDescription = `${workspaces} Validando existencia de la sesion de WSP: ${id}`; await createLog(messageDescription, false, null, null, codStatus.active); let client = allSessionsObject[id]; if (client && client.client) { messageDescription = `${workspaces} Existe la sesion de WSP: ${id}!!!`; await createLog(messageDescription, false, null, null, codStatus.active); sendResponses(res, codStatusHttp.ok, null, "Information processed successfully"); } else { messageDescription = `${workspaces} No existe la sesion de WSP: ${id}`; await createLog(messageDescription, false, null, null, codStatus.active); sendResponses(res, codStatusHttp.notFound, null, "Information processed successfully"); } messageDescription = `${workspaces} Finaliza exitosamente la validacion de la existencia de la sesion de WSP: ${id}`; await createLog(messageDescription, false, null, null, codStatus.active); } catch (error) { throw error; } } static async singleMessage(req, res, next, workspaces) { try { // ! Its pending to add validation that que user will be autorizated const { currentUser, body, file } = req; let messageDescription; messageDescription = `${workspaces} Inicia envio de mensajes individuales`; await createLog(messageDescription, false, null, currentUser.id, codStatus.active); const { id, countryCode, number, messages } = body; const client = allSessionsObject[id]; if (!client || !client.client || client.client.info === undefined) { messageDescription = `${workspaces} Falla el envio de mensajes porque el sistema no está listo`; let info = "The system is not ready yet"; await createLog(messageDescription, true, info, currentUser.id, codStatus.error); sendResponses(res, codStatusHttp.serviceUnavailable, null, info); } else { await WhastappsServices.validatePhone( client.client.info.me.user, `${currentUser.brand.countryCode}${currentUser.brand.phone}`, workspaces, currentUser, next ); if (file) { await WhastappsServices.validateSize(file, workspaces, currentUser, next); } await WhastappsServices.validateLimitPerDay(client.client.info.me.user, currentUser, next); if (countryCode == 0 || countryCode == " " || number == 0 || number == " ") { messageDescription = `${workspaces} Debe proporcionar un indicativo y/o numero diferente a espacio o cero`; await createLog(messageDescription, true, null, currentUser.id, codStatus.error); return next( new AppError( codStatusHttp.badRequest, "You must provide a different country code than empty or zero" ) ); } if (messages.length > maxCharMessages) { messageDescription = `${workspaces} Debe proporcionar un mensaje con caracteres inferiores a ${maxCharMessages}`; await createLog(messageDescription, true, null, currentUser.id, codStatus.error); return next( new AppError(codStatusHttp.badRequest, "The message has more than 180 characters") ); } let counterRow; let i = 0; await WhastappsServices.validateSuscription( client, countryCode, number, [messages], file, currentUser.clientId, counterRow, i, currentUser, workspaces ); messageDescription = `${workspaces} Finaliza exitosamente el envio de mensajes individuales`; await createLog(messageDescription, false, null, currentUser.id, codStatus.active); sendResponses(res, codStatusHttp.ok, null, "Information processed successfully"); } } catch (error) { throw error; } } static async masiveMessage(req, res, next, workspaces) { try { // ! Its pending to add validation that que user will be autorizated req.setTimeout(0); // ^ This is because I want the execution time of this function to be unlimited. const { currentUser, body, files } = req; let messageDescription; messageDescription = `${workspaces} Inicia envio de mensajes masivos`; await createLog(messageDescription, false, null, currentUser.id, codStatus.active); const { id, numbers, messages, flag } = body; // * This is because it will sometimes receive a booleans or strings (forms datas), so we need to format the data let validate = flag === "true" || flag === true ? true : false; const client = allSessionsObject[id]; if (!client || !client.client || client.client.info === undefined) { messageDescription = `${workspaces} Falla el envio de mensajes porque el sistema no está listo`; let info = "The system is not ready yet"; await createLog(messageDescription, true, info, currentUser.id, codStatus.error); sendResponses(res, codStatusHttp.serviceUnavailable, null, info); } else { let arrayMessages = messages.split("%-%"); if (arrayMessages.length !== 5) { messageDescription = `${workspaces} La cantidad de mensajes diferentes debe ser 5`; await createLog(messageDescription, false, null, currentUser.id, codStatus.active); return next( new AppError(codStatusHttp.badRequest, "You should have 5 different message types") ); } let flag = false; for (let index = 0; index < arrayMessages.length; index++) { const element = arrayMessages[index]; if (element.length > maxCharMessages) { flag = true; } } if (flag) { messageDescription = `${workspaces} Debe proporcionar mensajes con caracteres inferiores a ${maxCharMessages}`; await createLog(messageDescription, true, null, currentUser.id, codStatus.error); return next( new AppError(codStatusHttp.badRequest, "The message has more than 180 characters") ); } await WhastappsServices.validatePhone( client.client.info.me.user, `${currentUser.brand.countryCode}${currentUser.brand.phone}`, workspaces, currentUser, next ); let array = []; let file; if (validate) { // * Manual loading if (!numbers) { messageDescription = `${workspaces} Si desea utilizar este metodo de envio debe proporcionar un arreglo`; await createLog(messageDescription, true, null, currentUser.id, codStatus.error); return next(new AppError(codStatusHttp.badRequest, "Must provide a valid numbers")); } else if (!Array.isArray(numbers)) { messageDescription = `${workspaces} Si desea utilizar este metodo de envio debe proporcionar un arreglo`; await createLog(messageDescription, true, null, currentUser.id, codStatus.error); return next(new AppError(codStatusHttp.badRequest, "Numbers must be an Array")); } array = numbers; } else { // * Bulk upload per document let excel; files.forEach((element) => { let name = element.originalname; if (name.includes("listUsers")) { excel = element; } if (name.includes("attachedDocument")) { file = element; } }); if (excel) { array = await WhastappsServices.excelToJson(excel); } else if (typeof numbers === "string") { array = JSON.parse(numbers.trim()); } else { messageDescription = `${workspaces} Debe adjuntar un numeros para enviar mensajes`; await createLog(messageDescription, true, null, currentUser.id, codStatus.error); return next( new AppError(codStatusHttp.badRequest, "You must attach a number to send messages") ); } } if (file) { await WhastappsServices.validateSize(file, workspaces, currentUser, next); } if (array.length === 0) { messageDescription = `${workspaces} Debe agregar minimo un numero para enviar el mensaje`; await createLog(messageDescription, true, null, currentUser.id, codStatus.error); return next( new AppError( codStatusHttp.badRequest, "You must add at least 1 number to send messages" ) ); } if (array.length >= 901) { messageDescription = `${workspaces} La cantidad maxima para enviar mensajes son a 900 contactos`; await createLog(messageDescription, true, null, currentUser.id, codStatus.error); return next( new AppError( codStatusHttp.badRequest, "It is only allowed to send a maximum of 900 messages" ) ); } let counterRow; if (!validate) { counterRow = 2; } else { counterRow = 0; } for (let i = 0; i < array.length; i++) { if (array[i].phone || array[i].countryCode) { array[i].Telefono = array[i].phone; array[i].Pais = array[i].countryCode; } const numberRandom = (Math.random() * 10 + 4) * 1000; await new Promise((res) => setTimeout(res, numberRandom)); await WhastappsServices.validateLimitPerDay( client.client.info.me.user, currentUser, next ); let counterFlag = true; if (array[i].Telefono && array[i].Pais) { if ( !isNaN(array[i].Telefono) && !isNaN(array[i].Pais) && array[i].Telefono !== " " && array[i].Pais !== " " ) { await WhastappsServices.validateSuscription( client, array[i].Pais.toString(), array[i].Telefono.toString(), arrayMessages, file, currentUser.clientId, counterRow, i, currentUser, workspaces ); } else { messageDescription = `${workspaces} No se envio mensaje en ${ validate ? "el index" : "la fila" } ${counterRow}`; await createLog(messageDescription, true, null, currentUser.id, codStatus.error); counterRow += 1; counterFlag = false; } } else { if (counterFlag) { messageDescription = `${workspaces} No se envio mensaje en ${ validate ? "el index" : "la fila" } ${counterRow}`; await createLog(messageDescription, true, null, currentUser.id, codStatus.error); counterRow += 1; } } } messageDescription = `${workspaces} Finaliza exitosamente el envio de mensajes masivos`; await createLog(messageDescription, false, null, currentUser.id, codStatus.active); if (!validate) { files.forEach((element) => { fs.unlink(`./${element.path}`, function (err) { if (err) { console.log(err); } else { console.log("Archivo eliminado exitosamente"); } }); }); } sendResponses(res, codStatusHttp.ok, null, "Information processed successfully"); } } catch (error) { throw error; } } } module.exports = { WhastappsServices };