Atualização para habilitar ou desabilitar whatsapp oficial

pull/21/head
adriano 2023-09-16 11:45:44 -03:00
parent 6d525e4224
commit 996e182fb6
22 changed files with 763 additions and 411 deletions

View File

@ -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<Response> => {
// 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();

View File

@ -115,19 +115,31 @@ export const store = async (req: Request, res: Response): Promise<Response> => {
}
});
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();

View File

@ -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<Response> => {
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<Response> => {
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<Response> => {
const {
let {
name,
status,
isDefault,
@ -263,17 +310,39 @@ export const store = async (req: Request, res: Response): Promise<Response> => {
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<Response> => {
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!" };
}
};

View File

@ -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");
}
};

View File

@ -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");
}
};

View File

@ -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");
}
};

View File

@ -8,8 +8,6 @@ import { Op, where } from "sequelize";
import wbotByUserQueue from "../helpers/GetWbotByUserQueue";
// import WhatsQueueIndex from "./WhatsQueueIndex";
import { WhatsIndex } from "./LoadBalanceWhatsSameQueue";
interface Request {
@ -35,7 +33,7 @@ const GetDefaultWhatsApp = async ({
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) {

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -65,6 +65,17 @@ class Whatsapp extends Model<Whatsapp> {
@Column
phoneNumberId: string;
@Column
classification: string;
@Column
wabaId: string;
@Default(false)
@AllowNull
@Column
isOfficial: boolean;
@Default(false)
@AllowNull
@Column

View File

@ -16,6 +16,10 @@ const DeleteWhatsAppMessage = async (messageId: string): Promise<Message | any>
]
});
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.");
}
@ -32,18 +36,6 @@ const DeleteWhatsAppMessage = async (messageId: string): Promise<Message | any>
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 });
}

View File

@ -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.')
}
}
}

View File

@ -153,7 +153,7 @@ const verifyMediaMessage = async (
throw new Error("ERR_WAPP_DOWNLOAD_MEDIA");
}
const messageData = {
let messageData = {
id: msg.id.id,
ticketId: ticket.id,
contactId: msg.fromMe ? undefined : contact.id,
@ -165,10 +165,16 @@ const verifyMediaMessage = async (
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 {
@ -189,6 +195,77 @@ const verifyMediaMessage = async (
return newMessage;
};
// const verifyMediaMessage = async (
// msg: any,
// ticket: Ticket,
// contact: Contact,
// media: any,
// quotedMsg?: any
// ): Promise<Message | any> => {
// // 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,
ticket: Ticket,

View File

@ -13,6 +13,9 @@ interface Request {
farewellMessage?: string;
status?: string;
isDefault?: boolean;
phoneNumberId?: string;
wabaId?: string;
isOfficial?: boolean;
}
interface Response {
@ -29,10 +32,11 @@ const CreateWhatsAppService = async ({
greetingMessage,
farewellMessage,
isDefault = false,
isOfficial = false,
phoneNumberId,
wabaId
}: Request): Promise<Response> => {
try {
const schema = Yup.object().shape({
name: Yup.string()
.required()
@ -48,12 +52,11 @@ 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);
}
@ -70,7 +73,6 @@ const CreateWhatsAppService = async ({
});
if (oldDefaultWhatsapp) {
await oldDefaultWhatsapp.update({ isDefault: false });
}
}
@ -78,6 +80,12 @@ const CreateWhatsAppService = async ({
throw new AppError("ERR_WAPP_GREETING_REQUIRED");
}
const classification = isOfficial ? "GREEN" : null;
if (isOfficial) {
status = "CONNECTED";
}
const whatsapp = await Whatsapp.create(
{
name,
@ -86,7 +94,11 @@ const CreateWhatsAppService = async ({
urlApi,
greetingMessage,
farewellMessage,
isDefault
isDefault,
phoneNumberId,
wabaId,
isOfficial,
classification
},
{ include: ["queues"] }
);
@ -94,13 +106,10 @@ const CreateWhatsAppService = async ({
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;

View File

@ -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']
});
}

View File

@ -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<Response> => {
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,
@ -58,8 +60,6 @@ const UpdateWhatsAppService = async ({
queueIds = []
} = whatsappData;
try {
await schema.validate({ name, status, isDefault });
} catch (err: any) {
@ -85,10 +85,23 @@ const UpdateWhatsAppService = async ({
// 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({
@ -99,7 +112,11 @@ const UpdateWhatsAppService = async ({
urlApi,
greetingMessage,
farewellMessage,
isDefault
isDefault,
isOfficial,
phoneNumberId,
wabaId,
classification
});
// await insertOrUpeateWhatsCache(`whatsapp:${whatsapp.id}`, {
@ -114,12 +131,10 @@ const UpdateWhatsAppService = async ({
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;

View File

@ -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) {
@ -79,7 +72,7 @@ const ContactCreateTicketModal = ({ modalOpen, onClose, contactId }) => {
}, 500)
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 <LinearProgress />
// }
useEffect(() => {
console.log('selectedWhatsId: ', selectedWhatsId)
console.log('whatsQuee: ', whatsQueue)
}, [whatsQueue])
return (
<Dialog open={modalOpen} onClose={handleClose} maxWidth="xs" scroll="paper" classes={{ paper: classes.paper }}>

View File

@ -41,28 +41,6 @@ const useStyles = makeStyles(theme => ({
}))
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 = () => {
const classes = useStyles()

View File

@ -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,12 +69,17 @@ 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 () => {
@ -82,7 +87,9 @@ const WhatsAppModal = ({ open, onClose, whatsAppId }) => {
try {
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
@ -131,6 +149,7 @@ const WhatsAppModal = ({ open, onClose, whatsAppId }) => {
const handleClose = () => {
onClose()
setWhatsApp(initialState)
setIsOfficial(false)
}
return (
@ -189,8 +208,26 @@ const WhatsAppModal = ({ open, onClose, whatsAppId }) => {
}
label={i18n.t('whatsappModal.form.default')}
/>
{whatsAppOfficial &&
<FormControlLabel
control={
<Field
as={Switch}
color="primary"
name="isOfficial"
onClick={() => setIsOfficial(!isOfficial)}
checked={values.isOfficial}
/>
}
label={'Whatsapp Oficial'}
/>
}
</div>
<div className={classes.multFieldLine}>
{!isOfficial ? <div className={classes.multFieldLine}>
<Field
as={TextField}
label="url API"
@ -213,7 +250,39 @@ const WhatsAppModal = ({ open, onClose, whatsAppId }) => {
margin="dense"
className={classes.textField}
/>
</div>
</div> :
<div className={classes.multFieldLine}>
<Field
as={TextField}
label="Phone number id"
autoFocus
name="phoneNumberId"
error={touched.name && Boolean(errors.name)}
helperText={touched.name && errors.name}
variant="outlined"
margin="dense"
className={classes.textField}
/>
<Field
as={TextField}
label="WABA ID"
autoFocus
name="wabaId"
error={touched.name && Boolean(errors.name)}
helperText={touched.name && errors.name}
variant="outlined"
margin="dense"
className={classes.textField}
/>
</div>
}
</>
)}
/>

View File

@ -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 <CustomToolTip title={'Qualidade alta'}>
<FiberManualRecord style={{ color: green[500] }} />
</CustomToolTip>
if (classification === 'YELLOW')
return <CustomToolTip title={'Qualidade média'}>
<FiberManualRecord style={{ color: yellow[500] }} />
</CustomToolTip>
if (classification === 'RED')
return <CustomToolTip title={'Qualidade baixa'}>
<FiberManualRecord style={{ color: red[500] }} />
</CustomToolTip>
}
else {
return <CustomToolTip title={i18n.t('connections.toolTips.connected.title')}>
<SignalCellular4Bar style={{ color: green[500] }} />
</CustomToolTip>
}
}
const Connections = () => {
//--------
const { user } = useContext(AuthContext)
@ -118,8 +146,6 @@ const Connections = () => {
const [diskSpaceInfo, setDiskSpaceInfo] = useState({})
const [disabled, setDisabled] = useState(true)
const [settings, setSettings] = useState([])
const [buttons, setClicks] = 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 (
<Can
role={user.profile}
@ -352,7 +382,15 @@ const Connections = () => {
<SignalCellularConnectedNoInternet0Bar color="secondary" />
</CustomToolTip>
)}
{whatsApp.status === 'OPENING' && (
{whatsApp.status === 'OPENING' && whatsApp.isOfficial && (settings &&
settings.length > 0 &&
getSettingValue('whatsaAppCloudApi') &&
getSettingValue('whatsaAppCloudApi') === 'disabled') && (
<CustomToolTip title={'Whatsapp Cloud API está desativado'}>
<FiberManualRecord style={{ color: grey[500] }} />
</CustomToolTip>
)}
{whatsApp.status === 'OPENING' && !whatsApp.isOfficial && (
<CircularProgress size={24} className={classes.buttonProgress} />
)}
{whatsApp.status === 'qrcode' && (
@ -363,11 +401,11 @@ const Connections = () => {
<CropFree />
</CustomToolTip>
)}
{whatsApp.status === 'CONNECTED' && (
<CustomToolTip title={i18n.t('connections.toolTips.connected.title')}>
<SignalCellular4Bar style={{ color: green[500] }} />
</CustomToolTip>
whatsAppClasssification({ ...whatsApp, })
)}
{(whatsApp.status === 'TIMEOUT' || whatsApp.status === 'PAIRING') && (
<CustomToolTip
title={i18n.t('connections.toolTips.timeout.title')}
@ -397,8 +435,6 @@ const Connections = () => {
params: { status: 'status' },
})
setDisabled(false)
setClicks((buttons) =>
buttons.map((e) => {
return { id: e.id, disabled: false }
@ -464,6 +500,10 @@ const Connections = () => {
open={whatsAppModalOpen}
onClose={handleCloseWhatsAppModal}
whatsAppId={!qrModalOpen && selectedWhatsApp?.id}
whatsAppOfficial={(settings &&
settings.length > 0 &&
getSettingValue('whatsaAppCloudApi') &&
getSettingValue('whatsaAppCloudApi') === 'enabled') ? true : false}
/>
<ConfigModal
@ -482,7 +522,7 @@ const Connections = () => {
color="primary"
onClick={handleOpenConfigModal}
>
<Settings/>
<Settings />
</Button>
<Can
role={user.profile}
@ -567,13 +607,13 @@ const Connections = () => {
yes={() => <TableCell align="center">Restore</TableCell>}
/>
<Can
{/* <Can
role={user.profile}
perform="connection-button:show"
yes={() => (
<TableCell align="center">Session MB</TableCell>
)}
/>
/> */}
<TableCell align="center">
{i18n.t('connections.table.lastUpdate')}
@ -592,141 +632,162 @@ const Connections = () => {
) : (
<>
{whatsApps?.length > 0 &&
whatsApps.map((whatsApp) => (
<TableRow key={whatsApp.id}>
<TableCell align="center">
{whatsApp.name}
</TableCell>
whatsApps.map((whatsApp) => {
<TableCell align="center">
{renderStatusToolTips(whatsApp)}
</TableCell>
let disabledRow = {}
let disabled = false
<Can
role={user.profile}
perform="connection-button:show"
yes={() => (
<TableCell align="center">
{renderActionButtons(whatsApp)}
</TableCell>
)}
/>
if (whatsApp?.isOfficial && ((settings &&
settings.length > 0 &&
getSettingValue('whatsaAppCloudApi') &&
getSettingValue('whatsaAppCloudApi') === 'disabled'))) {
disabledRow = {
'opacity': '0.5',
// 'pointer-events': 'none'
}
disabled = true
}
<Can
role={user.profile}
perform="connection-button:show"
yes={() => (
<TableCell align="center">
<Button
disabled={
whatsApp.disabled || disabled
? true
: false
}
size="small"
variant="contained"
color="primary"
onClick={() =>
handleRestartWhatsAppSession(whatsApp)
}
>
Restore
</Button>
</TableCell>
)}
/>
return (
<TableRow key={whatsApp.id} style={disabledRow}>
<TableCell align="center">
{whatsApp.name}
</TableCell>
<Can
role={user.profile}
perform="connection-button:show"
yes={() => (
<TableCell align="center">
<CustomToolTip
title={'Informação da sessão em Megabytes'}
content={
'Tamanho do diretorio da sessão atualizado a cada 5 segundos'
}
>
<div>{whatsApp.sessionSize}</div>
</CustomToolTip>
</TableCell>
)}
/>
<TableCell align="center">
{renderStatusToolTips(whatsApp)}
</TableCell>
<TableCell align="center">
{format(
parseISO(whatsApp.updatedAt),
'dd/MM/yy HH:mm'
)}
</TableCell>
<TableCell align="center">
{whatsApp.isDefault && (
<div className={classes.customTableCell}>
<CheckCircle style={{ color: green[500] }} />
</div>
)}
</TableCell>
<TableCell align="center">
<Can
role={user.profile}
perform="show-icon-edit-whatsapp"
perform="connection-button:show"
yes={() => (
// disabled={
// whatsApp.disabled || disabled
// ? true
// : false
// }
<div
style={{
margin: 0,
padding: 0,
border: 'none',
display: 'inline',
}}
>
{(settings &&
settings.length > 0 &&
getSettingValue('editURA') &&
getSettingValue('editURA') ===
'enabled') |
(user.profile === 'master') ? (
<IconButton
<TableCell align="center">
{renderActionButtons(whatsApp)}
</TableCell>
)}
/>
<Can
role={user.profile}
perform="connection-button:show"
yes={() => (
<TableCell align="center">
{!whatsApp?.isOfficial &&
<Button
disabled={
whatsApp.disabled || disabled
? true
: false
}
size="small"
variant="contained"
color="primary"
onClick={() =>
handleEditWhatsApp(whatsApp)
handleRestartWhatsAppSession(whatsApp)
}
>
<Edit />
</IconButton>
) : (
<div></div>
)}
</div>
Restore
</Button>}
</TableCell>
)}
/>
<Can
{/* <Can
role={user.profile}
perform="btn-remove-whatsapp"
perform="connection-button:show"
yes={() => (
<IconButton
size="small"
onClick={(e) => {
handleOpenConfirmationModal(
'delete',
whatsApp.id
)
}}
>
<DeleteOutline />
</IconButton>
<TableCell align="center">
<CustomToolTip
title={'Informação da sessão em Megabytes'}
content={
'Tamanho do diretorio da sessão atualizado a cada 5 segundos'
}
>
<div>{whatsApp.sessionSize}</div>
</CustomToolTip>
</TableCell>
)}
/>
</TableCell>
</TableRow>
))}
/> */}
<TableCell align="center">
{format(
parseISO(whatsApp.updatedAt),
'dd/MM/yy HH:mm'
)}
</TableCell>
<TableCell align="center">
{whatsApp.isDefault && (
<div className={classes.customTableCell}>
<CheckCircle style={{ color: green[500] }} />
</div>
)}
</TableCell>
<TableCell align="center">
<Can
role={user.profile}
perform="show-icon-edit-whatsapp"
yes={() => (
// disabled={
// whatsApp.disabled || disabled
// ? true
// : false
// }
<div
style={{
margin: 0,
padding: 0,
border: 'none',
display: 'inline',
}}
>
{(settings &&
settings.length > 0 &&
getSettingValue('editURA') &&
getSettingValue('editURA') ===
'enabled') |
(user.profile === 'master') ? (
<IconButton
size="small"
onClick={() =>
handleEditWhatsApp(whatsApp)
}
>
<Edit />
</IconButton>
) : (
<div></div>
)}
</div>
)}
/>
<Can
role={user.profile}
perform="btn-remove-whatsapp"
yes={() => (
<IconButton
size="small"
onClick={(e) => {
handleOpenConfirmationModal(
'delete',
whatsApp.id
)
}}
>
<DeleteOutline />
</IconButton>
)}
/>
</TableCell>
</TableRow>
)
})}
</>
)}
</TableBody>

View File

@ -310,21 +310,6 @@ const Contacts = () => {
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)