Untitled

mail@pastecode.io avatar
unknown
javascript
a year ago
23 kB
4
Indexable
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 };