diff --git a/backend/package.json b/backend/package.json index 1e5bd13..d4a7105 100644 --- a/backend/package.json +++ b/backend/package.json @@ -23,6 +23,7 @@ "bcryptjs": "^2.4.3", "cookie-parser": "^1.4.5", "cors": "^2.8.5", + "cpf-cnpj-validator": "^1.0.3", "date-fns": "^2.30.0", "date-fns-tz": "^1.3.8", "dotenv": "^8.2.0", diff --git a/backend/src/controllers/IAMControllerEL.ts b/backend/src/controllers/IAMControllerEL.ts index b61291c..6b73e22 100644 --- a/backend/src/controllers/IAMControllerEL.ts +++ b/backend/src/controllers/IAMControllerEL.ts @@ -74,7 +74,65 @@ export const createUser = async ( .status(200) .json(response("1", `User ${user_id} created`, "1", "createUser")); }; +export const createMultiUsers = async( + req: Request, + res: Response +): Promise => { + const {data}: any = req.body; + if(data){ + try{ + for(let user of data){ + const nameSplit = user.name.split(' '); + const password = `${nameSplit[0]}.${nameSplit[1]}`; + const email = `${nameSplit[0]}.${nameSplit[1]}@omnihit.com`.toLowerCase(); + const create = await CreateUserService({ + email, + password, + name: user.name, + profile: "user", + ignoreThrow: true + }); + create?.error ?? console.log('erro ao criar user: ',user); + } + return res + .status(200) + .json(response("1", `Users created`, "1", "createUser")); + }catch(error){ + console.log(error); + return res + .status(500) + .json({ message: 'Error creating users'}); + } + }else{ + return res + .status(500) + .json({ message: 'Error creating users'}); + } +} + +export const allUsers = async ( + req: Request, + res: Response +): Promise => { + + const users = await User.findAll({attributes: ['email', 'name']}) + if(users){ + let index = 0; + for(let user of users){ + const nameSplit = user.name.split(' '); + const password = `${nameSplit[0]}.${nameSplit[1]}`; + users[index].password = password.toLowerCase(); + index ++; + } + return res + .status(200) + .json(response("1", "Success", users, "listAllUsers")); + }else{ + return res + .status(500) + } +} export const deleteUser = async ( req: Request, res: Response diff --git a/backend/src/database/migrations/20240610152456-add-column-isLGPDAccepted-to-contacts.ts b/backend/src/database/migrations/20240610152456-add-column-isLGPDAccepted-to-contacts.ts new file mode 100644 index 0000000..4be5505 --- /dev/null +++ b/backend/src/database/migrations/20240610152456-add-column-isLGPDAccepted-to-contacts.ts @@ -0,0 +1,15 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Contacts", "isLGPDAccepted", { + type: DataTypes.BOOLEAN, + allowNull: true, + defaultValue: false + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Contacts", "isLGPDAccepted"); + } +}; diff --git a/backend/src/libs/socket.ts b/backend/src/libs/socket.ts index 65382d6..368a46e 100644 --- a/backend/src/libs/socket.ts +++ b/backend/src/libs/socket.ts @@ -51,10 +51,10 @@ export const initIO = (httpServer: Server): SocketIO => { }); io.on("connection", socket => { - logger.info("Client Connected"); + //logger.info("Client Connected"); socket.on("joinWhatsSession", (whatsappId: string) => { - logger.info(`A client joined a joinWhatsSession channel: ${whatsappId}`); + //logger.info(`A client joined a joinWhatsSession channel: ${whatsappId}`); socket.join(`session_${whatsappId}`); }); @@ -139,7 +139,7 @@ export const initIO = (httpServer: Server): SocketIO => { lstOnline.push({ id: userId, status: "online", try: 0 }); lstOnlineAux.push({ id: userId }); - console.log(" 1 - PUSHED NEW USER ID 1: ", userId); + //console.log(" 1 - PUSHED NEW USER ID 1: ", userId); obj.listOnline = lstOnline; } else { @@ -154,7 +154,7 @@ export const initIO = (httpServer: Server): SocketIO => { if (index == -1) { lstOnline.push({ id: userId, status: "online", try: 0 }); - console.log(" 2 - PUSHED NEW USER ID: ", userId); + //console.log(" 2 - PUSHED NEW USER ID: ", userId); obj.listOnline = lstOnline; } else { @@ -204,30 +204,30 @@ export const initIO = (httpServer: Server): SocketIO => { }); socket.on("joinChatBox", (ticketId: string) => { - logger.info("A client joined a ticket channel"); + //logger.info("A client joined a ticket channel"); socket.join(ticketId); }); socket.on("joinNotification", () => { - logger.info("A client joined notification channel"); + //logger.info("A client joined notification channel"); socket.join("notification"); }); socket.on("joinTickets", (status: string) => { - logger.info(`A client joined to ${status} tickets channel.`); + //logger.info(`A client joined to ${status} tickets channel.`); socket.join(status); }); socket.on("disconnect", (data: any) => { - logger.info(`Client disconnected socket: ${data}`); + //logger.info(`Client disconnected socket: ${data}`); }); socket.on("disconnecting", async () => { - console.log("socket.rooms: ", socket.rooms); // the Set contains at least the socket ID + //console.log("socket.rooms: ", socket.rooms); // the Set contains at least the socket ID let rooms = socket.rooms; - console.log("rooms: ", rooms, " | rooms.size: ", rooms.size); + //console.log("rooms: ", rooms, " | rooms.size: ", rooms.size); if (rooms && rooms.size == 1) return; if (rooms && rooms.size == 2 && ![...rooms][1].startsWith("session_")) @@ -252,7 +252,7 @@ export const initIO = (httpServer: Server): SocketIO => { [...rooms][1].startsWith("session_") && whatsappIds.includes([...rooms][1].replace("session_", "")) ) { - console.log([...rooms][1]); + //console.log([...rooms][1]); let whatsappId = [...rooms][1].replace("session_", ""); diff --git a/backend/src/models/Contact.ts b/backend/src/models/Contact.ts index 8a2aeb7..cfd2ea5 100644 --- a/backend/src/models/Contact.ts +++ b/backend/src/models/Contact.ts @@ -44,6 +44,10 @@ class Contact extends Model { @Column isGroup: boolean; + @Default(false) + @Column + isLGPDAccepted: boolean; + @CreatedAt createdAt: Date; diff --git a/backend/src/routes/iamRoutesEL.ts b/backend/src/routes/iamRoutesEL.ts index 2181547..37fa7c0 100644 --- a/backend/src/routes/iamRoutesEL.ts +++ b/backend/src/routes/iamRoutesEL.ts @@ -10,7 +10,16 @@ iamRoutesEL.post( verifyAPIKey, IAMControllerEL.createUser ); - +iamRoutesEL.post( + "/iam/create-muilti-users", + verifyAPIKey, + IAMControllerEL.createMultiUsers +); +iamRoutesEL.get( + "/iam/allUsers", + verifyAPIKey, + IAMControllerEL.allUsers +); iamRoutesEL.put( "/iam/horacius/updateUser", verifyAPIKey, diff --git a/backend/src/services/ContactServices/UpdateContactService.ts b/backend/src/services/ContactServices/UpdateContactService.ts index 396d52e..9fd76f5 100644 --- a/backend/src/services/ContactServices/UpdateContactService.ts +++ b/backend/src/services/ContactServices/UpdateContactService.ts @@ -18,6 +18,7 @@ interface ContactData { name?: string; extraInfo?: ExtraInfo[]; queueIds?: number[]; + isLGPDAccepted?: boolean; } interface Request { @@ -30,13 +31,13 @@ const UpdateContactService = async ({ contactId }: Request): Promise => { try { - const { email, name, number, extraInfo, queueIds } = contactData; + const { email, name, number, extraInfo, queueIds, isLGPDAccepted } = contactData; // console.log('email, name, number, extraInfo: ', email, name, number, extraInfo) const contact = await Contact.findOne({ where: { id: contactId }, - attributes: ["id", "name", "number", "email", "profilePicUrl"], + attributes: ["id", "name", "number", "email", "profilePicUrl", "isLGPDAccepted"], include: ["extraInfo"] }); @@ -77,12 +78,20 @@ const UpdateContactService = async ({ throw new AppError("ERR_DUPLICATED_CONTACT"); } } - - await contact.update({ - name, - number, - email - }); + if(isLGPDAccepted != undefined){ + await contact.update({ + name, + number, + email, + isLGPDAccepted + }); + }else{ + await contact.update({ + name, + number, + email + }); + } if (queueIds) await AssociateContatctQueue(contact, queueIds); @@ -92,7 +101,7 @@ const UpdateContactService = async ({ // await contact.reload({ - attributes: ["id", "name", "number", "email", "profilePicUrl"], + attributes: ["id", "name", "number", "email", "profilePicUrl", "isLGPDAccepted"], include: ["extraInfo"] }); diff --git a/backend/src/services/WbotServices/wbotMessageListener.ts b/backend/src/services/WbotServices/wbotMessageListener.ts index 204b03b..a973630 100644 --- a/backend/src/services/WbotServices/wbotMessageListener.ts +++ b/backend/src/services/WbotServices/wbotMessageListener.ts @@ -6,6 +6,7 @@ import * as Sentry from "@sentry/node"; import { copyFolder } from "../../helpers/CopyFolder"; import { removeDir } from "../../helpers/DeleteDirectory"; import path from "path"; +import { cpf, cnpj } from 'cpf-cnpj-validator'; import { isHoliday, @@ -21,7 +22,8 @@ import { subMinutes, isSaturday, isSunday, - parseISO + parseISO, + differenceInDays } from "date-fns"; import ptBR from "date-fns/locale/pt-BR"; @@ -58,6 +60,7 @@ import fs from "fs"; import { StartWhatsAppSession } from "../../services/WbotServices/StartWhatsAppSession"; import { removeWbot } from "../../libs/wbot"; import { restartWhatsSession } from "../../helpers/RestartWhatsSession"; +import updateContactService from "../ContactServices/UpdateContactService" // test del import data_ura from "./ura"; @@ -184,8 +187,10 @@ const verifyMediaMessage = async ( phoneNumberId: msg?.phoneNumberId, fromAgent: false }; - if(messageData.mediaType === 'video' || messageData.mediaType === 'audio' && getSettingValue('blockAudioVideoMedia')?.value === 'enabled'){ - mediaAuthorized = false; + if(getSettingValue('blockAudioVideoMedia')?.value === 'enabled'){ + if( messageData.mediaType === 'video' || messageData.mediaType === 'audio' ){ + mediaAuthorized = false; + } } if (msg?.fromMe) { messageData = { ...messageData, fromAgent: true }; @@ -838,7 +843,7 @@ const handleMessage = async ( ) { const filteredUsers = await findByContain("user:*", "name", msg?.body); - if (filteredUsers && filteredUsers.length > 0) { + if (filteredUsers && filteredUsers.length > 0 && ticket?.status === 'pending') { if (botInfo.isOnQueue) { transferTicket(filteredUsers[0].name, wbot, ticket, true); return; @@ -875,6 +880,21 @@ const handleMessage = async ( !msg.fromMe && ticket.userId == botInfo.userIdBot ) { + if(msg?.body?.toLowerCase() === 'sair') { + await UpdateTicketService({ + ticketData: { + status: "closed" + }, + ticketId: ticket.id + }) + await SendWhatsAppMessage({ + body: `Ok\nAtendimento finalizado. A Gertec agradece o seu contato! + `, + ticket, + number: `${contact.number}@c.us` + }); + return; + } const repet: any = await mostRepeatedPhrase(ticket.id); console.log("repet.occurrences: ", repet.occurrences); @@ -890,11 +910,11 @@ const handleMessage = async ( }); } else { console.log("MSG body: ", msg.body); - + if (msg.type != "chat") { botSendMessage( ticket, - `Desculpe, nao compreendi!\nEnvie apenas texto quando estiver interagindo com o bot!\n _Digite *0* para voltar ao menu principal._` + `Desculpe, nao compreendi!\nEnvie apenas texto quando estiver interagindo com o bot!\n _Digite *#* para voltar ao menu anterior._` ); return; } @@ -907,7 +927,7 @@ const handleMessage = async ( return; } - const menuMsg: any = await menu(msg.body, wbot.id, contact.id); + const menuMsg: any = await menu(msg.body, wbot.id, contact.id, ticket); console.log("menuMsg: ", menuMsg); @@ -939,7 +959,7 @@ const handleMessage = async ( }, ticketId: ticket.id }); - const menuMsg: any = await menu(msg.body, wbot.id, contact.id); + const menuMsg: any = await menu(msg.body, wbot.id, contact.id, ticket); await botSendMessage(ticket, menuMsg.value); } @@ -962,7 +982,7 @@ const handleMessage = async ( ticketId: ticket.id }); - const menuMsg: any = await menu(msg.body, wbot.id, contact.id); + const menuMsg: any = await menu(msg.body, wbot.id, contact.id, ticket); await botSendMessage(ticket, menuMsg.value); @@ -1003,20 +1023,35 @@ const handleMessage = async ( } }; -const menu = async (userTyped: string, whatsappId: any, contactId: any) => { +const menu = async (userTyped: string, whatsappId: any, contactId: any, ticket: any) => { let lastId = await findObject(whatsappId, contactId, "ura"); const data: any = await get({ key: "ura", parse: true }); - + let contactData = await Contact.findOne({ + where: { id: contactId }, + attributes: ["id", "name", "number", "email", "profilePicUrl", "isLGPDAccepted"], + include: ["extraInfo"] + }); console.log("lastId[0]: ", lastId[0]); + if (!lastId[0]) { - await createObject({ - whatsappId, - contactId, - identifier: "ura", - value: data[1].id, - history: `|${data[1].id}` - }); + if(contactData && contactData.isLGPDAccepted && !isMoreThan30Days(contactData?.updatedAt?.toString())){ + await createObject({ + whatsappId, + contactId, + identifier: "ura", + value: data[3].id, + history: `|${data[3].id}` + }); + }else{ + await createObject({ + whatsappId, + contactId, + identifier: "ura", + value: data[1].id, + history: `|${data[1].id}` + }); + } } lastId = await findObject(whatsappId, contactId, "ura"); @@ -1037,7 +1072,7 @@ const menu = async (userTyped: string, whatsappId: any, contactId: any) => { if (!option && userTyped != "0" && userTyped != "#") { if (!existSubMenu()) { - const response = await mainOptionsMenu(userTyped); + const response = await mainOptionsMenu(userTyped, ticket); if (response) return response; else { let uraOptionSelected = await findObject( @@ -1047,7 +1082,7 @@ const menu = async (userTyped: string, whatsappId: any, contactId: any) => { ); uraOptionSelected = uraOptionSelected[4].split("|"); - + if (uraOptionSelected.length == 1) { await createObject({ whatsappId, @@ -1083,8 +1118,14 @@ const menu = async (userTyped: string, whatsappId: any, contactId: any) => { // if (option) { + let LGPDSaves = contactData?.isLGPDAccepted; + let response: any = data.find((o: any) => o.idmaster == option.id); - // console.log(" RESPONSE OPTION: ", response, " | OPTION: ", option); + + if(!LGPDSaves && option.id === '1400' && userTyped.toLowerCase() === 'ok' && contactData){ + contactData.isLGPDAccepted = true; + await updateContactService({contactData, contactId}); + } let history: any = await historyUra(whatsappId, contactId, response.id); @@ -1102,11 +1143,11 @@ const menu = async (userTyped: string, whatsappId: any, contactId: any) => { whatsappId, contactId, identifier: "ura", - value: data[1].id, + value: '1426', history: `|${data[1].id}` }); - - return data[1]; + let response = data.find((obj: any) => obj.id === '1426'); + return response; } else if (userTyped == "#") { return await backUra(whatsappId, contactId, data); } @@ -1120,9 +1161,16 @@ const menu = async (userTyped: string, whatsappId: any, contactId: any) => { return false; } - async function mainOptionsMenu(userTyped: any) { + async function mainOptionsMenu(userTyped: any, ticket: any) { let currentMenu = await findObject(whatsappId, contactId, "ura"); + let contactData = await Contact.findOne({ + where: { id: contactId }, + attributes: ["id", "name", "number", "email", "profilePicUrl", "isLGPDAccepted"], + include: ["extraInfo"] + }); + let LGPDSaves = false; + const menuValues = data .filter((m: any) => m.idmaster == currentMenu[3]) .map((m: any) => m.value); @@ -1132,10 +1180,16 @@ const menu = async (userTyped: string, whatsappId: any, contactId: any) => { o.value.toLowerCase() == userTyped.toLowerCase() && menuValues.includes(userTyped.toLowerCase()) ); - - if (menuOption) { - let response = data.find((o: any) => o.idmaster == menuOption.id); - if (response) { + if (menuOption || currentMenu[3] === '1398') { + let response : any; + if (currentMenu[3] === '1398'){ + let option = data.find((o: any) => o.id == currentMenu[3]); + response = data.find((o: any) => o.id == option.idnext); + + if (response) response.value = `Seja bem-vindo(a) ao WhatsApp Gertec.\nQual o seu nome?`; + }else response = data.find((o: any) => o.idmaster == menuOption.id); + + if (response) { let history = await historyUra(whatsappId, contactId, response.id); await createObject({ @@ -1148,10 +1202,93 @@ const menu = async (userTyped: string, whatsappId: any, contactId: any) => { return response; } - } + }else if(menuValues && menuValues.includes('CPF') + || menuValues.includes('CNPJ') + || menuValues.includes('name') + || menuValues.includes('email')) + { + let isValid = false; + + if(menuValues.includes('CPF')){ + if(userTyped.length === 11){ + isValid = validateCPF(userTyped); + }else if(userTyped.length === 14){ + isValid = validateCNPJ(userTyped); + } + }else if(menuValues.includes('CNPJ') && userTyped.length === 14) { + isValid = validateCNPJ(userTyped); + }else if(menuValues.includes('name')){ + + if (contactData && contactData.name != userTyped) { + contactData.name = userTyped; + await updateContactService({contactData, contactId}); + } + botSendMessage( + ticket, + `O protocolo desse atendimento é: ${ticket?.id}.` + ); + + isValid = true; + }else{ + isValid = true; + } + if(!isValid){ + let response = data.find((o: any) => o.id == currentMenu[3]); + if(response){ + response.value = `CNPJ/CPF inválido. Por favor, digite um CNPJ/CPF válido.`; + let history = await historyUra(whatsappId, contactId, response.id); + + await createObject({ + whatsappId, + contactId, + identifier: "ura", + value: response.id, + history + }); + return response; + } + } + + const idOption = data.find((o:any)=> o.idmaster === currentMenu[3] ); + if (idOption != -1 ){ + + let response = data.find((o: any) => o.idmaster == idOption.id); + + if (response?.id === '1426' && contactData) response.value = `Certo *${contactData.name}*, ${response.value}`; + + let history = await historyUra(whatsappId, contactId, response.id); + + await createObject({ + whatsappId, + contactId, + identifier: "ura", + value: response.id, + history + }); + return response; + } + } } }; +const validateCPF = (cpfNumber: string): boolean => { + return cpf.isValid(cpfNumber); +}; + +const validateCNPJ = (cnpjNumber: string): boolean => { + return cnpj.isValid(cnpjNumber); +}; +const validateEmail = (email: string): boolean => { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + return emailRegex.test(email); +}; +const isMoreThan30Days = (updatedAt: string): boolean => { + const updatedDate = parseISO(updatedAt); + const today = new Date(); + const diffDays = differenceInDays(today, updatedDate); + return diffDays >= 30; +}; + const handleMsgAck = async ( msg_id: any, ack: any, diff --git a/frontend/src/components/MessageInput/index.js b/frontend/src/components/MessageInput/index.js index 9f0b895..f20b25a 100644 --- a/frontend/src/components/MessageInput/index.js +++ b/frontend/src/components/MessageInput/index.js @@ -247,7 +247,7 @@ const MessageInput = ({ ticketStatus, ticketLastMessage, ticketIsRemote }) => { setInputMessage(ticketLastMessage) } else { - setInputMessage("") + //setInputMessage("") } }, [countTicketMsg, ticketIsRemote, ticketLastMessage])