diff --git a/backend/src/controllers/SettingController.ts b/backend/src/controllers/SettingController.ts index 639cd75..90ae788 100644 --- a/backend/src/controllers/SettingController.ts +++ b/backend/src/controllers/SettingController.ts @@ -8,6 +8,8 @@ import ListSettingsService from "../services/SettingServices/ListSettingsService import loadSettings from "../helpers/LoadSettings"; import updateSettingTicket from "../services/SettingServices/UpdateSettingTicket"; import SettingTicket from "../models/SettingTicket"; +import Whatsapp from "../models/Whatsapp"; +import whatsappOfficialNumberInfo from "../helpers/WhatsappOfficialNumberInfo"; export const index = async (req: Request, res: Response): Promise => { // if (req.user.profile !== "master") { @@ -76,16 +78,14 @@ export const updateTicketSettings = async ( }); } - return res - .status(200) - .json({ - outBusinessHours, - ticketExpiration, - weekend, - saturday, - sunday, - holiday - }); + return res.status(200).json({ + outBusinessHours, + ticketExpiration, + weekend, + saturday, + sunday, + holiday + }); }; export const update = async ( @@ -103,6 +103,40 @@ export const update = async ( value }); + if (key && key == "whatsaAppCloudApi") { + let whatsapps: any = await Whatsapp.findAll(); + + if (whatsapps) { + for (let i in whatsapps) { + const { id, wabaId, isOfficial } = whatsapps[i]; + + if (isOfficial && wabaId) { + try { + const whatsapp = await Whatsapp.findByPk(id); + if (whatsapp) { + if (value == "disabled") { + whatsapp.update({ status: "OPENING" }); + } else if (value == "enabled") { + const info = await whatsappOfficialNumberInfo(wabaId); + if (info) { + whatsapp.update({ + classification: info.quality_rating, + status: "CONNECTED" + }); + } + } + } + } catch (error) { + console.log( + "error on try update classification number from oficial whatsapp in SettingControllers.ts: ", + error + ); + } + } + } + } + } + loadSettings(); const io = getIO(); diff --git a/backend/src/controllers/TicketController.ts b/backend/src/controllers/TicketController.ts index f7a308a..735dd69 100644 --- a/backend/src/controllers/TicketController.ts +++ b/backend/src/controllers/TicketController.ts @@ -115,19 +115,31 @@ export const store = async (req: Request, res: Response): Promise => { } }); - if (ticket) { - await UpdateTicketService({ - ticketData: { status: "open", userId: userId, queueId }, - ticketId: ticket.id - }); + if (getSettingValue("whatsaAppCloudApi")?.value == "enabled") { + if (ticket) { + await UpdateTicketService({ + ticketData: { status: "closed" }, + ticketId: ticket.id + }); + ticket = null; + } } else { - ticket = await CreateTicketService({ - contactId, - status, - userId, - queueId, - whatsappId - }); + if (ticket) { + await UpdateTicketService({ + ticketData: { status: "open", userId: userId, queueId }, + ticketId: ticket.id + }); + } + } + + if(!ticket){ + ticket = await CreateTicketService({ + contactId, + status, + userId, + queueId, + whatsappId + }); } const io = getIO(); diff --git a/backend/src/controllers/WhatsAppController.ts b/backend/src/controllers/WhatsAppController.ts index 0a73278..9993989 100644 --- a/backend/src/controllers/WhatsAppController.ts +++ b/backend/src/controllers/WhatsAppController.ts @@ -36,6 +36,10 @@ import ShowUserService from "../services/UserServices/ShowUserService"; import fs from "fs"; import receiveWhatsAppMediaOfficialAPI from "../helpers/ReceiveWhatsAppMediaOfficialAPI"; +import whatsappOfficialAPI from "../helpers/WhatsappOfficialAPI"; +import whatsappOfficialNumberInfo from "../helpers/WhatsappOfficialNumberInfo"; +import { getSettingValue } from "../helpers/WhaticketSettings"; + interface WhatsappData { name: string; queueIds: number[]; @@ -45,10 +49,45 @@ interface WhatsappData { farewellMessage?: string; status?: string; isDefault?: boolean; + isOfficial?: boolean; + phoneNumberId?: string; + wabaId?: string; } +let count: number = 0; + export const index = async (req: Request, res: Response): Promise => { - const whatsapps = await ListWhatsAppsService(); + let whatsapps = await ListWhatsAppsService(); + + if (getSettingValue("whatsaAppCloudApi")?.value == "enabled") { + // Atualizar isso quando tiver tempo + if (count > 12) count = 0; + if (count == 0) { + for (let i in whatsapps) { + const { id, wabaId, isOfficial } = whatsapps[i]; + + if (isOfficial && wabaId) { + try { + const info = await whatsappOfficialNumberInfo(wabaId); + if (info) { + const whatsapp = await Whatsapp.findByPk(id); + + if (whatsapp) { + whatsapp.update({ + classification: info.quality_rating + }); + whatsapps[i].classification = info.quality_rating; + } + } + } catch (error) { + console.log('error on try update classification number from oficial whatsapp in WhatsappController.ts: ', error) + } + } + } + } + console.log("count: ", count); + count++; + } return res.status(200).json(whatsapps); }; @@ -59,7 +98,15 @@ export const whatsAppOfficialMatchQueue = async ( ): Promise => { const { userId, queueId }: any = req.query; - const whatsapps = await GetDefaultWhatsApp({ userId, queueId }); + let whatsapps = await GetDefaultWhatsApp({ userId, queueId }); + + if (whatsapps && Array.isArray(whatsapps)) { + const uniqueWhatsApps = whatsapps.filter( + (whatsapp, index, self) => + index === self.findIndex(w => w.number === whatsapp.number) + ); + whatsapps = uniqueWhatsApps; + } return res.status(200).json(whatsapps); }; @@ -255,7 +302,7 @@ export const weebhook = async ( }; export const store = async (req: Request, res: Response): Promise => { - const { + let { name, status, isDefault, @@ -263,17 +310,39 @@ export const store = async (req: Request, res: Response): Promise => { farewellMessage, queueIds, url, - urlApi + urlApi, + phoneNumberId, + wabaId, + isOfficial }: WhatsappData = req.body; if (req.user.profile !== "master") { throw new AppError("ERR_NO_PERMISSION", 403); } - let validate = validatePhoneName(name); + const invalid = checkWhatsAppData({ + urlApi, + isOfficial, + phoneNumberId, + wabaId + }); - if (validate) { - return res.status(200).json({ message: validate }); + if (invalid) { + return res.status(400).json(invalid); + } + + if (isOfficial) { + urlApi = ""; + url = ""; + } else if (!isOfficial) { + phoneNumberId = ""; + wabaId = ""; + } + + let invalidPhoneName = validatePhoneName(name); + + if (invalidPhoneName) { + return res.status(200).json({ message: invalidPhoneName }); } const { whatsapp, oldDefaultWhatsapp } = await CreateWhatsAppService({ @@ -284,19 +353,22 @@ export const store = async (req: Request, res: Response): Promise => { isDefault, greetingMessage, farewellMessage, - queueIds + queueIds, + phoneNumberId, + wabaId, + isOfficial }); console.log("whatsapp.id: ", whatsapp.id); - postData(`${whatsapp.urlApi}/api/session`, { - app_name: process.env.APP_NAME, - whatsappId: whatsapp.id, - number: getNumberFromName(name), - client_url: `${process.env.BACKEND_URL_RAW}:${process.env.PORT}` - }); - - // StartWhatsAppSession(whatsapp); + if (!isOfficial) { + postData(`${whatsapp.urlApi}/api/session`, { + app_name: process.env.APP_NAME, + whatsappId: whatsapp.id, + number: getNumberFromName(name), + client_url: `${process.env.BACKEND_URL_RAW}:${process.env.PORT}` + }); + } const io = getIO(); io.emit("whatsapp", { @@ -329,12 +401,31 @@ export const update = async ( const { whatsappId } = req.params; const whatsappData = req.body; - let validate = validatePhoneName(whatsappData.name); + let invalidPhoneName = validatePhoneName(whatsappData.name); - console.log("validate", validate); + if (invalidPhoneName) { + return res.status(200).json({ message: invalidPhoneName }); + } - if (validate) { - return res.status(200).json({ message: validate }); + const { urlApi, isOfficial, phoneNumberId, wabaId } = whatsappData; + + const invalid = checkWhatsAppData({ + urlApi, + isOfficial, + phoneNumberId, + wabaId + }); + + if (invalid) { + return res.status(400).json(invalid); + } + + if (isOfficial) { + whatsappData.urlApi = ""; + whatsappData.url = ""; + } else if (!isOfficial) { + whatsappData.phoneNumberId = ""; + whatsappData.wabaId = ""; } const { whatsapp, oldDefaultWhatsapp } = await UpdateWhatsAppService({ @@ -342,12 +433,14 @@ export const update = async ( whatsappId }); - postData(`${whatsapp.urlApi}/api/session`, { - app_name: process.env.APP_NAME, - whatsappId: whatsapp.id, - number: getNumberFromName(whatsapp.name), - client_url: `${process.env.BACKEND_URL_RAW}:${process.env.PORT}` - }); + if (!whatsappData?.isOfficial) { + postData(`${whatsapp.urlApi}/api/session`, { + app_name: process.env.APP_NAME, + whatsappId: whatsapp.id, + number: getNumberFromName(whatsapp.name), + client_url: `${process.env.BACKEND_URL_RAW}:${process.env.PORT}` + }); + } const io = getIO(); io.emit("whatsapp", { @@ -377,10 +470,12 @@ export const remove = async ( const whatsapp: any = await Whatsapp.findByPk(whatsappId, { raw: true }); - postData(`${whatsapp.urlApi}/api/session/del`, { - app_name: process.env.APP_NAME, - whatsappId: whatsappId - }); + if (!whatsapp?.isOfficial) { + postData(`${whatsapp.urlApi}/api/session/del`, { + app_name: process.env.APP_NAME, + whatsappId: whatsappId + }); + } await DeleteWhatsAppService(whatsappId); @@ -407,3 +502,25 @@ export const remove = async ( return res.status(200).json({ message: "Whatsapp deleted." }); }; + +interface WhatsappDataValidate { + urlApi?: string; + isOfficial?: boolean; + phoneNumberId?: string; + wabaId?: string; +} + +const checkWhatsAppData = ({ + urlApi, + isOfficial, + phoneNumberId, + wabaId +}: WhatsappDataValidate) => { + if (isOfficial && (!phoneNumberId || phoneNumberId.trim() == "")) { + return { message: "Phone number Id is required!" }; + } else if (isOfficial && (!wabaId || wabaId.trim() == "")) { + return { message: "WABA ID is required!" }; + } else if (!isOfficial && (!urlApi || urlApi.trim() == "")) { + return { message: "urlApi is required!" }; + } +}; diff --git a/backend/src/database/migrations/20230914142250-add-column-whatsapp-is-official.ts b/backend/src/database/migrations/20230914142250-add-column-whatsapp-is-official.ts new file mode 100644 index 0000000..87a9f66 --- /dev/null +++ b/backend/src/database/migrations/20230914142250-add-column-whatsapp-is-official.ts @@ -0,0 +1,15 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Whatsapps", "isOfficial", { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Whatsapps", "isOfficial"); + } +}; diff --git a/backend/src/database/migrations/20230914180602-add-column-whatsapp-waba-id.ts b/backend/src/database/migrations/20230914180602-add-column-whatsapp-waba-id.ts new file mode 100644 index 0000000..ce60043 --- /dev/null +++ b/backend/src/database/migrations/20230914180602-add-column-whatsapp-waba-id.ts @@ -0,0 +1,14 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Whatsapps", "wabaId", { + type: DataTypes.STRING, + allowNull: true + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Whatsapps", "wabaId"); + } +}; diff --git a/backend/src/database/migrations/20230914183537-add-column-whatsapp-classification.ts b/backend/src/database/migrations/20230914183537-add-column-whatsapp-classification.ts new file mode 100644 index 0000000..e1a17f3 --- /dev/null +++ b/backend/src/database/migrations/20230914183537-add-column-whatsapp-classification.ts @@ -0,0 +1,14 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Whatsapps", "classification", { + type: DataTypes.STRING, + allowNull: true + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Whatsapps", "classification"); + } +}; diff --git a/backend/src/helpers/GetDefaultWhatsApp.ts b/backend/src/helpers/GetDefaultWhatsApp.ts index 5af78d5..5c1033f 100644 --- a/backend/src/helpers/GetDefaultWhatsApp.ts +++ b/backend/src/helpers/GetDefaultWhatsApp.ts @@ -6,9 +6,7 @@ import UserQueue from "../models/UserQueue"; import { Op, where } from "sequelize"; -import wbotByUserQueue from "../helpers/GetWbotByUserQueue"; - -// import WhatsQueueIndex from "./WhatsQueueIndex"; +import wbotByUserQueue from "../helpers/GetWbotByUserQueue"; import { WhatsIndex } from "./LoadBalanceWhatsSameQueue"; @@ -30,12 +28,12 @@ const GetDefaultWhatsApp = async ({ if (!defaultWhatsapp) { if (userId) { - let whatsapps = await wbotByUserQueue({ userId, queueId }); + let whatsapps = await wbotByUserQueue({ userId, queueId }); if (userId && queueId) { if (whatsapps.length > 1) { let whatsAppOfficial: any = whatsapps.find( - (w: any) => w.phoneNumberId != null + (w: any) => w.phoneNumberId && w.phoneNumberId.trim().length > 0 ); if (whatsAppOfficial) { diff --git a/backend/src/helpers/TrySendMessageMultiSession.ts b/backend/src/helpers/TrySendMessageMultiSession.ts deleted file mode 100644 index ecc0069..0000000 --- a/backend/src/helpers/TrySendMessageMultiSession.ts +++ /dev/null @@ -1,49 +0,0 @@ - -import Ticket from "../models/Ticket"; -import ListWhatsAppsNumber from "../services/WhatsappService/ListWhatsAppsNumber"; -import GetTicketWbot from "./GetTicketWbot"; - -const sendMessageMultiSession = async (ticket: Ticket, body?: any, quotedMsgSerializedId?: any, sendSeen?: boolean) => { - - let sentMessage: any = '' - - const listWhatsapp: any = await ListWhatsAppsNumber(ticket.whatsappId, 'CONNECTED') - - if (listWhatsapp.length > 0) { - - for (let w = 0; w < listWhatsapp.length; w++) { - - if(listWhatsapp[w].id == ticket.whatsappId) continue - - try { - - console.log('CHANGE THE WHATSAPP SESSION ID: ', listWhatsapp[w].id) - - await ticket.update({ whatsappId: listWhatsapp[w].id }) - - const wbot = await GetTicketWbot(ticket); - - if (sendSeen) { - - await wbot.sendSeen(`${ticket.contact.number}@${ticket.isGroup ? "g" : "c"}.us`); - - } - else if (body) { - - sentMessage = await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g" : "c"}.us`, body, { quotedMessageId: quotedMsgSerializedId, linkPreview: false }); - - } - - break - - } catch (error) { - console.log('Cannot send send the message using the whatsapp id: ', listWhatsapp[w].id, ' | error: ', error) - } - - } - } - - return sentMessage -}; - -export default sendMessageMultiSession; diff --git a/backend/src/helpers/WhatsappOfficialNumberInfo.ts b/backend/src/helpers/WhatsappOfficialNumberInfo.ts new file mode 100644 index 0000000..77ae2b3 --- /dev/null +++ b/backend/src/helpers/WhatsappOfficialNumberInfo.ts @@ -0,0 +1,21 @@ +import whatsappOfficialAPI from "./WhatsappOfficialAPI"; + +async function whatsappOfficialNumberInfo(wabaId: string) { + try { + const { data } = await whatsappOfficialAPI.get( + `/${process.env.VERSION}/${wabaId}/phone_numbers` + ); + console.log("data: ", data); + + if (data && Object.keys(data).length > 0) { + return data.data[0]; + } + } catch (error) { + console.log( + `There was an error into whatsappOfficialNumberInfo method : ${error}` + ); + } + + return null; +} +export default whatsappOfficialNumberInfo; diff --git a/backend/src/helpers/old_EndPointQuery.ts b/backend/src/helpers/old_EndPointQuery.ts index bfe975b..a749b32 100644 --- a/backend/src/helpers/old_EndPointQuery.ts +++ b/backend/src/helpers/old_EndPointQuery.ts @@ -1,39 +1,28 @@ const fsPromises = require("fs/promises"); -const fs = require('fs') -import axios from 'axios'; +const fs = require("fs"); +import axios from "axios"; import * as https from "https"; const endPointQuery = async (url: string, data: any) => { + let response: any = null; - let response: any = null + try { + response = await axios.post(url, data); - try { - - response = await axios.post(url, data); - - console.log(`TEST URL CLIENT POST ROUTE: ${url} | STATUS CODE: ${response.status}`); - - - } catch (err: any) { - - if (err.response) { - // The client was given an error response (5xx, 4xx) - // console.log('err.response: ', err.response) - console.log('err.response: ', err.response) - - // return { data: err.response.data, status: err.response.status } - - } else if (err.request) { - // The client never received a response, and the request was never left - console.log('err.request: ', err.request) - } else { - // Anything else - console.error(`Erro ao consultar endpoint ${url}: ${err}`); - } + console.log( + `TEST URL CLIENT POST ROUTE: ${url} | STATUS CODE: ${response.status}` + ); + } catch (err: any) { + if (err.response) { + console.log("err.response: ", err.response); + } else if (err.request) { + console.log("err.request: ", err.request); + } else { + console.error(`Erro ao consultar endpoint ${url}: ${err}`); } + } - return response + return response; +}; -} - -export default endPointQuery; \ No newline at end of file +export default endPointQuery; diff --git a/backend/src/models/Whatsapp.ts b/backend/src/models/Whatsapp.ts index c98e622..28cae6a 100644 --- a/backend/src/models/Whatsapp.ts +++ b/backend/src/models/Whatsapp.ts @@ -65,6 +65,17 @@ class Whatsapp extends Model { @Column phoneNumberId: string; + @Column + classification: string; + + @Column + wabaId: string; + + @Default(false) + @AllowNull + @Column + isOfficial: boolean; + @Default(false) @AllowNull @Column diff --git a/backend/src/services/WbotServices/DeleteWhatsAppMessage.ts b/backend/src/services/WbotServices/DeleteWhatsAppMessage.ts index c3785e5..e066809 100644 --- a/backend/src/services/WbotServices/DeleteWhatsAppMessage.ts +++ b/backend/src/services/WbotServices/DeleteWhatsAppMessage.ts @@ -15,6 +15,10 @@ const DeleteWhatsAppMessage = async (messageId: string): Promise } ] }); + + if(message && message?.phoneNumberId){ + throw new AppError("Mensagens enviada pela API Oficial do Whatsapp não são deletadas!"); + } if (!message) { throw new AppError("No message found with this ID."); @@ -30,19 +34,7 @@ const DeleteWhatsAppMessage = async (messageId: string): Promise number: `${ticket.contact.number}@${ticket.isGroup ? "g" : "c"}.us`, messageId: messageId, limit: limit - }) - - // console.log('messageToDelete.data.data: ',messageToDelete.data.data) - - // const { ticket } = message; - - // const messageToDelete = await GetWbotMessage(ticket, messageId); - - // try { - // await messageToDelete.delete(true); - // } catch (err) { - // throw new AppError("ERR_DELETE_WAPP_MSG"); - // } + }) if (messageToDelete && messageToDelete.data.data) { await message.update({ isDeleted: true }); diff --git a/backend/src/services/WbotServices/SendWhatsAppMessage.ts b/backend/src/services/WbotServices/SendWhatsAppMessage.ts index 767af76..ec7ee77 100644 --- a/backend/src/services/WbotServices/SendWhatsAppMessage.ts +++ b/backend/src/services/WbotServices/SendWhatsAppMessage.ts @@ -21,7 +21,6 @@ import ListWhatsAppsNumber from "../WhatsappService/ListWhatsAppsNumber"; import { getWbot } from "../../libs/wbot"; import { json } from "sequelize/types"; -import sendMessageMultiSession from "../../helpers/TrySendMessageMultiSession"; import { restartWhatsSession } from "../../helpers/RestartWhatsSession"; // import { insertOrUpeateWhatsCache, searchWhatsappCache } from "../../helpers/WhatsCache"; import GetDefaultWhatsApp from "../../helpers/GetDefaultWhatsApp"; @@ -83,6 +82,7 @@ const SendWhatsAppMessage = async ({ if (!listWhatsapp) { listWhatsapp = await ListWhatsAppsNumber(ticket.whatsappId, "CONNECTED"); + console.log("@@@@@@@@@@@ listWhatsapp: ", listWhatsapp); } if ( @@ -106,20 +106,21 @@ const SendWhatsAppMessage = async ({ listWhatsapp.whatsapps.length == 0 && listWhatsapp.whatsapp.status != "CONNECTED" ) { - console.log("listWhatsapp.whatsapps == 0"); - whatsapps = await wbotByUserQueue({ userId: ticket.userId }); - console.log("============> The whatsapps: ", whatsapps); - if (whatsapps.length > 0) { + whatsapps = whatsapps.filter((w: any) => !w?.isOfficial); + if (whatsapps.length > 1) { await ticket.update({ whatsappId: whatsapps[+WhatsIndex(whatsapps)].id }); - } else { + } else if (whatsapps && whatsapps.length == 1) { await ticket.update({ whatsappId: whatsapps[0].id }); } + else{ + throw new Error('Sessão de Whatsapp desconectada! Entre em contato com o suporte.') + } } } diff --git a/backend/src/services/WbotServices/wbotMessageListener.ts b/backend/src/services/WbotServices/wbotMessageListener.ts index 3aa8ec4..206659f 100644 --- a/backend/src/services/WbotServices/wbotMessageListener.ts +++ b/backend/src/services/WbotServices/wbotMessageListener.ts @@ -152,8 +152,8 @@ const verifyMediaMessage = async ( if (!media) { throw new Error("ERR_WAPP_DOWNLOAD_MEDIA"); } - - const messageData = { + + let messageData = { id: msg.id.id, ticketId: ticket.id, contactId: msg.fromMe ? undefined : contact.id, @@ -164,12 +164,18 @@ const verifyMediaMessage = async ( mediaType: media.mimetype.split("/")[0], quotedMsgId: quotedMsg }; + if (!ticket?.phoneNumberId) { if (!media.filename) { const ext = media.mimetype.split("/")[1].split(";")[0]; media.filename = `${new Date().getTime()}.${ext}`; - } + messageData = { + ...messageData, + mediaUrl: media.filename, + body: media.filename + }; + } try { await writeFileAsync( @@ -188,6 +194,77 @@ const verifyMediaMessage = async ( return newMessage; }; + +// const verifyMediaMessage = async ( +// msg: any, +// ticket: Ticket, +// contact: Contact, +// media: any, +// quotedMsg?: any +// ): Promise => { +// // const quotedMsg = await verifyQuotedMessage(msg); + +// // const media = await msg.downloadMedia(); + +// if (!media) { +// throw new Error("ERR_WAPP_DOWNLOAD_MEDIA"); +// } + +// console.log( +// "MEDIA.FILENAME: ", +// media.fileName, +// " | msg.fromMe: ", +// msg.fromMe +// ); + +// if (!media.filename) { +// console.log("No file name -----------------------------------------"); + +// const ext = media.mimetype.split("/")[1].split(";")[0]; +// media.filename = `${new Date().getTime()}.${ext}`; +// } + +// try { +// // await writeFileAsync( +// // join(__dirname, "..", "..", "..", "public", media.filename), +// // media.data, +// // "base64" +// // ); + +// console.log("FROM wbotMessageListener.ts media.filename: ", media.filename); + +// await writeFileAsync( +// join(__dirname, "..", "..", "..", "..", "..", "public", media.filename), +// media.data, +// "base64" +// ); +// } catch (err) { +// Sentry.captureException(err); +// logger.error(`There was an error: wbotMessageLitener.ts: ${err}`); +// } + +// const messageData = { +// id: msg.id.id, +// ticketId: ticket.id, +// contactId: msg.fromMe ? undefined : contact.id, +// body: msg.body || media.filename, +// fromMe: msg.fromMe, +// read: msg.fromMe, +// mediaUrl: media.filename, +// mediaType: media.mimetype.split("/")[0], +// quotedMsgId: quotedMsg +// // quotedMsgId: quotedMsg?.id +// }; + +// await ticket.update({ lastMessage: msg.body || media.filename }); +// const newMessage = await CreateMessageService({ messageData }); + +// return newMessage; +// }; + + + + const verifyMessage = async ( msg: any, diff --git a/backend/src/services/WhatsappService/CreateWhatsAppService.ts b/backend/src/services/WhatsappService/CreateWhatsAppService.ts index 91c7370..2693709 100644 --- a/backend/src/services/WhatsappService/CreateWhatsAppService.ts +++ b/backend/src/services/WhatsappService/CreateWhatsAppService.ts @@ -13,6 +13,9 @@ interface Request { farewellMessage?: string; status?: string; isDefault?: boolean; + phoneNumberId?: string; + wabaId?: string; + isOfficial?: boolean; } interface Response { @@ -28,11 +31,12 @@ const CreateWhatsAppService = async ({ queueIds = [], greetingMessage, farewellMessage, - isDefault = false, + isDefault = false, + isOfficial = false, + phoneNumberId, + wabaId }: Request): Promise => { - try { - const schema = Yup.object().shape({ name: Yup.string() .required() @@ -48,36 +52,40 @@ const CreateWhatsAppService = async ({ return !nameExists; } ), - isDefault: Yup.boolean().required(), - urlApi: Yup.string().required() + isDefault: Yup.boolean().required() }); - + try { - await schema.validate({ name, status, isDefault, urlApi }); + await schema.validate({ name, status, isDefault }); } catch (err: any) { throw new AppError(err.message); } - + const whatsappFound = await Whatsapp.findOne(); - + isDefault = !whatsappFound; - + let oldDefaultWhatsapp: Whatsapp | null = null; - + if (isDefault) { oldDefaultWhatsapp = await Whatsapp.findOne({ where: { isDefault: true } }); if (oldDefaultWhatsapp) { await oldDefaultWhatsapp.update({ isDefault: false }); - } } - + if (queueIds.length > 1 && !greetingMessage) { throw new AppError("ERR_WAPP_GREETING_REQUIRED"); } - + + const classification = isOfficial ? "GREEN" : null; + + if (isOfficial) { + status = "CONNECTED"; + } + const whatsapp = await Whatsapp.create( { name, @@ -86,21 +94,22 @@ const CreateWhatsAppService = async ({ urlApi, greetingMessage, farewellMessage, - isDefault + isDefault, + phoneNumberId, + wabaId, + isOfficial, + classification }, { include: ["queues"] } ); - + await AssociateWhatsappQueue(whatsapp, queueIds); - + return { whatsapp, oldDefaultWhatsapp }; - - } catch (error: any) { - console.error('===> Error on CreateWhatsAppService.ts file: \n', error) + } catch (error: any) { + console.error("===> Error on CreateWhatsAppService.ts file: \n", error); throw new AppError(error.message); } - - }; export default CreateWhatsAppService; diff --git a/backend/src/services/WhatsappService/ListWhatsAppsNumber.ts b/backend/src/services/WhatsappService/ListWhatsAppsNumber.ts index 3c8a072..8bb9758 100644 --- a/backend/src/services/WhatsappService/ListWhatsAppsNumber.ts +++ b/backend/src/services/WhatsappService/ListWhatsAppsNumber.ts @@ -12,14 +12,14 @@ const ListWhatsAppsNumber = async ( if (status) { whatsapps = await Whatsapp.findAll({ raw: true, - where: { number: whatsapp.number, status: status }, - attributes: ["id", "number", "status", "isDefault", "url"] + where: { number: whatsapp.number, status: status, }, + attributes: ["id", "number", "status", "isDefault", "url", 'isOfficial'] }); } else { whatsapps = await Whatsapp.findAll({ raw: true, where: { number: whatsapp.number }, - attributes: ["id", "number", "status", "isDefault", "url"] + attributes: ["id", "number", "status", "isDefault", "url", 'isOfficial'] }); } diff --git a/backend/src/services/WhatsappService/UpdateWhatsAppService.ts b/backend/src/services/WhatsappService/UpdateWhatsAppService.ts index 04336df..982407b 100644 --- a/backend/src/services/WhatsappService/UpdateWhatsAppService.ts +++ b/backend/src/services/WhatsappService/UpdateWhatsAppService.ts @@ -9,8 +9,6 @@ import AssociateWhatsappQueue from "./AssociateWhatsappQueue"; import { getWbot } from "../../libs/wbot"; import { restartWhatsSession } from "../../helpers/RestartWhatsSession"; - - interface WhatsappData { name?: string; url?: string; @@ -18,6 +16,9 @@ interface WhatsappData { status?: string; session?: string; isDefault?: boolean; + isOfficial?: boolean; + phoneNumberId?: string; + wabaId?: string; greetingMessage?: string; farewellMessage?: string; queueIds?: number[]; @@ -37,19 +38,20 @@ const UpdateWhatsAppService = async ({ whatsappData, whatsappId }: Request): Promise => { - try { - const schema = Yup.object().shape({ name: Yup.string().min(2), status: Yup.string(), isDefault: Yup.boolean() - }); - - const { + }); + + let { name, status, isDefault, + phoneNumberId, + wabaId, + isOfficial, url, urlApi, session, @@ -57,21 +59,19 @@ const UpdateWhatsAppService = async ({ farewellMessage, queueIds = [] } = whatsappData; - - - + try { await schema.validate({ name, status, isDefault }); } catch (err: any) { throw new AppError(err.message); } - + if (queueIds.length > 1 && !greetingMessage) { throw new AppError("ERR_WAPP_GREETING_REQUIRED"); } - + let oldDefaultWhatsapp: Whatsapp | null = null; - + if (isDefault) { oldDefaultWhatsapp = await Whatsapp.findOne({ where: { isDefault: true, id: { [Op.not]: whatsappId } } @@ -80,17 +80,30 @@ const UpdateWhatsAppService = async ({ await oldDefaultWhatsapp.update({ isDefault: false }); } } - + const whatsapp = await ShowWhatsAppService(whatsappId); - + // console.log('############## whatsapp: ', JSON.parse(JSON.stringify(whatsapp))) - - if (name && !name.includes(whatsapp.number) && whatsapp.status === 'CONNECTED') { - + + if ( + name && + !name.includes(whatsapp.number) && + whatsapp.status === "CONNECTED" && + !whatsapp.isOfficial + ) { throw new AppError("ERR_WAPP_WRONG_SESSION_NAME"); - } - + + const classification = isOfficial ? "GREEN" : null; + + if (isOfficial) { + status = "CONNECTED"; + } + + if (whatsapp.isOfficial && !isOfficial) { + status = "OPENING"; + } + await whatsapp.update({ name, status, @@ -99,9 +112,13 @@ const UpdateWhatsAppService = async ({ urlApi, greetingMessage, farewellMessage, - isDefault + isDefault, + isOfficial, + phoneNumberId, + wabaId, + classification }); - + // await insertOrUpeateWhatsCache(`whatsapp:${whatsapp.id}`, { // name, // status, @@ -110,16 +127,14 @@ const UpdateWhatsAppService = async ({ // farewellMessage, // isDefault // }) - - await AssociateWhatsappQueue(whatsapp, queueIds); - - return { whatsapp, oldDefaultWhatsapp }; + await AssociateWhatsappQueue(whatsapp, queueIds); + + return { whatsapp, oldDefaultWhatsapp }; } catch (error: any) { - console.error('===> Error on UpdateWhatsAppService.ts file: \n', error) + console.error("===> Error on UpdateWhatsAppService.ts file: \n", error); throw new AppError(error.message); } - }; export default UpdateWhatsAppService; diff --git a/frontend/src/components/ContactCreateTicketModal/index.js b/frontend/src/components/ContactCreateTicketModal/index.js index a2fb86a..bbc199b 100644 --- a/frontend/src/components/ContactCreateTicketModal/index.js +++ b/frontend/src/components/ContactCreateTicketModal/index.js @@ -8,7 +8,6 @@ import Select from "@material-ui/core/Select" import FormControl from "@material-ui/core/FormControl" import InputLabel from "@material-ui/core/InputLabel" import MenuItem from "@material-ui/core/MenuItem" -import LinearProgress from "@material-ui/core/LinearProgress" import { makeStyles } from "@material-ui/core" import DialogActions from "@material-ui/core/DialogActions" @@ -21,9 +20,6 @@ import { AuthContext } from "../../context/Auth/AuthContext" import toastError from "../../errors/toastError" -import { WhatsAppsContext } from "../../context/WhatsApp/WhatsAppsContext" - - import api from "../../services/api" const useStyles = makeStyles((theme) => ({ @@ -40,7 +36,6 @@ const useStyles = makeStyles((theme) => ({ const ContactCreateTicketModal = ({ modalOpen, onClose, contactId }) => { const { user } = useContext(AuthContext) - const { whatsApps } = useContext(WhatsAppsContext) let isMounted = useRef(true) @@ -65,8 +60,6 @@ const ContactCreateTicketModal = ({ modalOpen, onClose, contactId }) => { const { data } = await api.get("/whatsapp/official/matchQueueUser", { params: { userId: user.id, queueId: selectedQueue }, }) - console.log('DATA: ', data) - setQueues(data) } catch (err) { @@ -77,9 +70,9 @@ const ContactCreateTicketModal = ({ modalOpen, onClose, contactId }) => { fetchMatchQueueUserOfficialWhatsapp() }, 500) - return () => clearTimeout(delayDebounceFn) + return () => clearTimeout(delayDebounceFn) - }, [user]) + }, [user, selectedQueue]) const handleClose = () => { onClose() @@ -87,9 +80,7 @@ const ContactCreateTicketModal = ({ modalOpen, onClose, contactId }) => { const handleSaveTicket = useCallback(async (contactId, userId, queueId, selectedWhatsId, whatsQueue, queues) => { - console.log(`contactId: ${contactId}, userId: ${userId}, queueId: ${queueId}, selectedWhatsId: ${selectedWhatsId}, whatsQueue: ${whatsQueue}, queues: ${queues}`) - - if (queues && queues.length == 1 && queues[0].disable) { + if (queues && queues.length === 1 && queues[0].disable) { toast.warn('Não é possível criar um Ticket porque a fila a qual você esta adicionado(a) não está associado a nenhum numero!') return } @@ -137,15 +128,11 @@ const ContactCreateTicketModal = ({ modalOpen, onClose, contactId }) => { useEffect(() => { - if (selectedQueue && selectedQueue.length === 0 || !selectedQueue) { + if (selectedQueue && (selectedQueue.length === 0 || !selectedQueue)) { setDisabled(true) setWhatsQueue(null) } - // if (modalOpen && queues.length <= 1) { - // handleSaveTicket(contactId, user.id, selectedQueue, selectedWhatsId, whatsQueue, queues) - // } - if (!Array.isArray(whatsQueue)) { setSelectedWhatsId(null) setWhatsQueue(null) @@ -170,6 +157,8 @@ const ContactCreateTicketModal = ({ modalOpen, onClose, contactId }) => { const { data } = await api.get("/whatsapp/official/matchQueue", { params: { userId: user.id, queueId: selectedQueue }, }) + console.log('WHATSAPP DATA: ', data) + setWhatsQueue(data) setDisabled(false) @@ -186,10 +175,10 @@ const ContactCreateTicketModal = ({ modalOpen, onClose, contactId }) => { }, [selectedQueue, user.id]) - - // if (modalOpen && queues.length <= 1) { - // return - // } + useEffect(() => { + console.log('selectedWhatsId: ', selectedWhatsId) + console.log('whatsQuee: ', whatsQueue) + }, [whatsQueue]) return ( diff --git a/frontend/src/components/NotificationsPopOver/index.js b/frontend/src/components/NotificationsPopOver/index.js index 34ce098..b14678d 100644 --- a/frontend/src/components/NotificationsPopOver/index.js +++ b/frontend/src/components/NotificationsPopOver/index.js @@ -38,29 +38,7 @@ const useStyles = makeStyles(theme => ({ noShadow: { boxShadow: "none !important", }, -})) - - -let _fifo - -// const onlineEmitter = async (socket, user) => { - -// try { -// clearInterval(_fifo); - -// socket.emit("online", user.id) - -// } catch (error) { -// console.log('error on onlineEmitter: ', error) -// } -// finally { -// _fifo = setInterval(onlineEmitter, 3000); -// } -// } - -// _fifo = setInterval(onlineEmitter, 3000); - - +})) const NotificationsPopOver = () => { diff --git a/frontend/src/components/WhatsAppModal/index.js b/frontend/src/components/WhatsAppModal/index.js index b2b8e40..4468aaf 100644 --- a/frontend/src/components/WhatsAppModal/index.js +++ b/frontend/src/components/WhatsAppModal/index.js @@ -60,7 +60,7 @@ const SessionSchema = Yup.object().shape({ .required('Required'), }) -const WhatsAppModal = ({ open, onClose, whatsAppId }) => { +const WhatsAppModal = ({ open, onClose, whatsAppId, whatsAppOfficial }) => { const classes = useStyles() const initialState = { name: '', @@ -69,20 +69,27 @@ const WhatsAppModal = ({ open, onClose, whatsAppId }) => { greetingMessage: '', farewellMessage: '', isDefault: false, + isOfficial: false, + phoneNumberId: '', + wabaId: '' } const { user } = useContext(AuthContext) const [whatsApp, setWhatsApp] = useState(initialState) const [selectedQueueIds, setSelectedQueueIds] = useState([]) + const [isOfficial, setIsOfficial] = useState(false) + useEffect(() => { const fetchSession = async () => { if (!whatsAppId) return try { - const { data } = await api.get(`whatsapp/${whatsAppId}`) + const { data } = await api.get(`whatsapp/${whatsAppId}`) + setWhatsApp(data) + setIsOfficial(data?.isOfficial) const whatsQueueIds = data.queues?.map((queue) => queue.id) setSelectedQueueIds(whatsQueueIds) @@ -94,6 +101,17 @@ const WhatsAppModal = ({ open, onClose, whatsAppId }) => { }, [whatsAppId]) const handleSaveWhatsApp = async (values) => { + + console.log('values1: ', values) + + const { isOfficial } = values + + if (!isOfficial) { + values.phoneNumberId = '' + values.wabaId = '' + } + + const whatsappData = { ...values, queueIds: selectedQueueIds } let response = null @@ -126,11 +144,12 @@ const WhatsAppModal = ({ open, onClose, whatsAppId }) => { } catch (err) { toastError(err) } - } + } const handleClose = () => { onClose() setWhatsApp(initialState) + setIsOfficial(false) } return ( @@ -189,8 +208,26 @@ const WhatsAppModal = ({ open, onClose, whatsAppId }) => { } label={i18n.t('whatsappModal.form.default')} /> + + {whatsAppOfficial && + setIsOfficial(!isOfficial)} + checked={values.isOfficial} + /> + } + label={'Whatsapp Oficial'} + /> + } + + -
+ + {!isOfficial ?
{ margin="dense" className={classes.textField} /> -
+
: + +
+ + + +
+ + + + } + + )} /> diff --git a/frontend/src/pages/Connections/index.js b/frontend/src/pages/Connections/index.js index 3e188ee..e4055e6 100644 --- a/frontend/src/pages/Connections/index.js +++ b/frontend/src/pages/Connections/index.js @@ -5,9 +5,9 @@ import { format, parseISO } from 'date-fns' import openSocket from 'socket.io-client' import { makeStyles } from '@material-ui/core/styles' -import { green } from '@material-ui/core/colors' +import { green, red, yellow, grey } from '@material-ui/core/colors' -import Settings from "@material-ui/icons/Settings"; +import Settings from "@material-ui/icons/Settings" import { Button, @@ -27,6 +27,7 @@ import { CheckCircle, SignalCellularConnectedNoInternet2Bar, SignalCellularConnectedNoInternet0Bar, + FiberManualRecord, SignalCellular4Bar, CropFree, DeleteOutline, @@ -103,6 +104,33 @@ const CustomToolTip = ({ title, content, children }) => { ) } +const whatsAppClasssification = ({ isOfficial, classification }) => { + + if (isOfficial && classification) { + + if (classification === 'GREEN') + return + + + + if (classification === 'YELLOW') + return + + + + if (classification === 'RED') + return + + + } + else { + return + + + } + +} + const Connections = () => { //-------- const { user } = useContext(AuthContext) @@ -116,9 +144,7 @@ const Connections = () => { const [selectedWhatsApp, setSelectedWhatsApp] = useState(null) const [confirmModalOpen, setConfirmModalOpen] = useState(false) - const [diskSpaceInfo, setDiskSpaceInfo] = useState({}) - - const [disabled, setDisabled] = useState(true) + const [diskSpaceInfo, setDiskSpaceInfo] = useState({}) const [settings, setSettings] = useState([]) @@ -139,6 +165,7 @@ const Connections = () => { const fetchSession = async () => { try { const { data } = await api.get('/settings') + setSettings(data.settings) } catch (err) { toastError(err) @@ -149,7 +176,6 @@ const Connections = () => { const getSettingValue = (key) => { const { value } = settings.find((s) => s.key === key) - return value } @@ -162,6 +188,7 @@ const Connections = () => { } const handleRestartWhatsAppSession = async (whatsapp) => { + try { whatsapp.disabled = true @@ -280,6 +307,9 @@ const Connections = () => { } const renderActionButtons = (whatsApp) => { + + if (whatsApp.isOfficial) return + return ( { )} - {whatsApp.status === 'OPENING' && ( + {whatsApp.status === 'OPENING' && whatsApp.isOfficial && (settings && + settings.length > 0 && + getSettingValue('whatsaAppCloudApi') && + getSettingValue('whatsaAppCloudApi') === 'disabled') && ( + + + + )} + {whatsApp.status === 'OPENING' && !whatsApp.isOfficial && ( )} {whatsApp.status === 'qrcode' && ( @@ -363,11 +401,11 @@ const Connections = () => { )} + {whatsApp.status === 'CONNECTED' && ( - - - + whatsAppClasssification({ ...whatsApp, }) )} + {(whatsApp.status === 'TIMEOUT' || whatsApp.status === 'PAIRING') && ( { try { await api.post(`/restartwhatsappsession/0`, { params: { status: 'status' }, - }) - - setDisabled(false) + }) setClicks((buttons) => buttons.map((e) => { @@ -464,11 +500,15 @@ const Connections = () => { open={whatsAppModalOpen} onClose={handleCloseWhatsAppModal} whatsAppId={!qrModalOpen && selectedWhatsApp?.id} + whatsAppOfficial={(settings && + settings.length > 0 && + getSettingValue('whatsaAppCloudApi') && + getSettingValue('whatsaAppCloudApi') === 'enabled') ? true : false} /> @@ -482,7 +522,7 @@ const Connections = () => { color="primary" onClick={handleOpenConfigModal} > - + { yes={() => Restore} /> - ( Session MB )} - /> + /> */} {i18n.t('connections.table.lastUpdate')} @@ -592,141 +632,162 @@ const Connections = () => { ) : ( <> {whatsApps?.length > 0 && - whatsApps.map((whatsApp) => ( - - - {whatsApp.name} - + whatsApps.map((whatsApp) => { - - {renderStatusToolTips(whatsApp)} - + let disabledRow = {} + let disabled = false - ( - - {renderActionButtons(whatsApp)} - - )} - /> + if (whatsApp?.isOfficial && ((settings && + settings.length > 0 && + getSettingValue('whatsaAppCloudApi') && + getSettingValue('whatsaAppCloudApi') === 'disabled'))) { + disabledRow = { + 'opacity': '0.5', + // 'pointer-events': 'none' + } + disabled = true + } - ( - - - - )} - /> + return ( + + + {whatsApp.name} + - ( - - -
{whatsApp.sessionSize}
-
-
- )} - /> + + {renderStatusToolTips(whatsApp)} + - - {format( - parseISO(whatsApp.updatedAt), - 'dd/MM/yy HH:mm' - )} - - - - {whatsApp.isDefault && ( -
- -
- )} -
- - ( - // disabled={ - // whatsApp.disabled || disabled - // ? true - // : false - // } -
- {(settings && - settings.length > 0 && - getSettingValue('editURA') && - getSettingValue('editURA') === - 'enabled') | - (user.profile === 'master') ? ( - + {renderActionButtons(whatsApp)} + + )} + /> + + ( + + + {!whatsApp?.isOfficial && +
+ Restore + } + + +
)} /> - ( - { - handleOpenConfirmationModal( - 'delete', - whatsApp.id - ) - }} - > - - + + +
{whatsApp.sessionSize}
+
+
)} - /> -
- - ))} + /> */} + + + {format( + parseISO(whatsApp.updatedAt), + 'dd/MM/yy HH:mm' + )} + + + + {whatsApp.isDefault && ( +
+ +
+ )} +
+ + + ( + // disabled={ + // whatsApp.disabled || disabled + // ? true + // : false + // } +
+ {(settings && + settings.length > 0 && + getSettingValue('editURA') && + getSettingValue('editURA') === + 'enabled') | + (user.profile === 'master') ? ( + + handleEditWhatsApp(whatsApp) + } + > + + + ) : ( +
+ )} +
+ )} + /> + + ( + { + handleOpenConfirmationModal( + 'delete', + whatsApp.id + ) + }} + > + + + )} + /> +
+ + ) + })} )} diff --git a/frontend/src/pages/Contacts/index.js b/frontend/src/pages/Contacts/index.js index 1fe8066..b8965c2 100644 --- a/frontend/src/pages/Contacts/index.js +++ b/frontend/src/pages/Contacts/index.js @@ -308,23 +308,8 @@ const Contacts = () => { const handleCloseCreateTicketModal = () => { setIsCreateTicketModalOpen(false) - } - - // const handleSaveTicket = async (contactId) => { - // if (!contactId) return; - // setLoading(true); - // try { - // const { data: ticket } = await api.post("/tickets", { - // contactId: contactId, - // userId: user?.id, - // status: "open", - // }); - // history.push(`/tickets/${ticket.id}`); - // } catch (err) { - // toastError(err); - // } - // setLoading(false); - // }; + } + const hadleEditContact = (contactId) => { setSelectedContactId(contactId)