Merge branch 'ominihit_hit' into dialogflow_ansata3

adriano 2023-07-13 15:28:00 -03:00
commit 77b7ed393f
39 changed files with 1602 additions and 945 deletions

0
TEST_SERVER1.7z 100644
View File

View File

@ -211,7 +211,8 @@ app.post('/api/session', async function (req, res) {
session_num = numbered_sessions.map((e) => parseInt((e.name.split('->')[e.name.split('->').length - 1]).trim().match(/\d+/)[0])) session_num = numbered_sessions.map((e) => parseInt((e.name.split('->')[e.name.split('->').length - 1]).trim().match(/\d+/)[0]))
console.log('session_num', session_num) console.log('session_num', session_num)
} }
let index = 1; let index = 1;

View File

@ -19,8 +19,8 @@ const express = require('express');
const FormData = require('form-data'); const FormData = require('form-data');
// const { MessageMedia } = require('./node_modules/whatsapp-web.js/src/structures'); // const { MessageMedia } = require('./node_modules/whatsapp-web.js/src/structures');
let whatsappWebGlobalPath = path.join(process.env.NODE_PATH, 'whatsapp-web.js', '/src/structures'); let whatsappWebGlobalPath = path.join(process.env.NODE_PATH, 'whatsapp-web.js', '/src/structures');
whatsappWebGlobalPath = whatsappWebGlobalPath.replace(':','') whatsappWebGlobalPath = whatsappWebGlobalPath.replace(':', '')
console.log('whatsappWebGlobalPath: ', whatsappWebGlobalPath) console.log('whatsappWebGlobalPath: ', whatsappWebGlobalPath)
console.log('process.env.NODE_PATH: ', process.env.NODE_PATH) console.log('process.env.NODE_PATH: ', process.env.NODE_PATH)
@ -178,17 +178,22 @@ client.on("qr", async qr => {
asking_qrcode = true asking_qrcode = true
await new Promise((resolve, reject) => {
dbcc.query("UPDATE Whatsapps SET qrcode = ?, status = ?, retries = ? where id = ?", [qr, 'qrcode', 0, process.env.WHATSAPP_ID], dbcc.query("UPDATE Whatsapps SET qrcode = ?, status = ?, retries = ? where id = ?", [qr, 'qrcode', 0, process.env.WHATSAPP_ID],
function (err, result) { function (err, result) {
if (err)
console.log("ERROR: " + err);
// else if (err) {
// console.log('myslq result: ', result); console.log("ERROR: " + err);
}); reject(err)
}
else {
resolve(result)
}
});
})
let url = process.env.CLIENT_URL + '/whatsapp/connection/qrcode' let url = process.env.CLIENT_URL + '/whatsapp/connection/qrcode'
@ -271,15 +276,25 @@ client.on("ready", async () => {
dbcc.query("UPDATE Whatsapps SET qrcode = ?, status = ?, retries = ?, number = ? where id = ?", ["", 'CONNECTED', 0, client.info["wid"]["user"], process.env.WHATSAPP_ID], await new Promise((resolve, reject) => {
dbcc.query("UPDATE Whatsapps SET qrcode = ?, status = ?, retries = ?, number = ? where id = ?", ["", 'CONNECTED', 0, client.info["wid"]["user"], process.env.WHATSAPP_ID],
function (err, result) {
if (err) {
console.log("ERROR: " + err);
reject(err)
}
else {
resolve(result)
}
});
})
function (err, result) {
if (err)
console.log("ERROR: " + err);
// else
// console.log('myslq result: ', result);
});
let url = process.env.CLIENT_URL + '/whatsapp/connection/qrcode' let url = process.env.CLIENT_URL + '/whatsapp/connection/qrcode'
@ -384,7 +399,7 @@ client.on("message_ack", async (msg, ack) => {
}); });
socketIo.on('send_message', async data => { socketIo.on('send_message', async data => {
console.log('#') console.log('#')
console.log('--------------> send_message from number: ', mobileuid); console.log('--------------> send_message from number: ', mobileuid);
console.log('--------------> send_message to number: ', data.msg.number); console.log('--------------> send_message to number: ', data.msg.number);
@ -400,7 +415,7 @@ socketIo.on('send_message', async data => {
socketIo.on('send_media', async data => { socketIo.on('send_media', async data => {
console.log('#') console.log('#')
console.log('--------------> send_message from number: ', mobileuid); console.log('--------------> send_message from number: ', mobileuid);
console.log('--------------> send_message to number: ', data.msg.number); console.log('--------------> send_message to number: ', data.msg.number);
@ -414,7 +429,7 @@ socketIo.on('send_media', async data => {
if (media && !media.filename) if (media && !media.filename)
media.filename = data.msg.media.filename media.filename = data.msg.media.filename
const sentMessage = await client.sendMessage(data.msg.number, media, { sendAudioAsVoice: data.msg.sendAudioAsVoice }); const sentMessage = await client.sendMessage(data.msg.number, media, { sendAudioAsVoice: data.msg.sendAudioAsVoice });
// const fullFilename = process.cwd() + process.env.MEDIA_DOWNLOAD_IN + data.msg.media.filename; // const fullFilename = process.cwd() + process.env.MEDIA_DOWNLOAD_IN + data.msg.media.filename;
@ -590,6 +605,37 @@ app.post('/api/restore', async (req, res) => {
res.status(200).json({ message: "ok" }); res.status(200).json({ message: "ok" });
}) })
app.post('/api/sendSeen', async (req, res) => {
let stat
const { number } = req.body
try {
stat = await client.getState();
// await syncUnreadMessages(client)
const wbotChat = await client.getChatById(number);
wbotChat.sendSeen();
// const chatMessages = await wbotChat.fetchMessages({ limit: 100 });
// console.log('=============> wbotChat: ', chatMessages)
} catch (err) {
let terr = err.message;
stat = (terr.search('Session closed') > -1 ? 'SESSIONCLOSED' : 'UNKNOWN')
}
res.status(200).json({ message: "ok" });
})
app.get('/api/connection/status', async (req, res) => { app.get('/api/connection/status', async (req, res) => {
let stat let stat
@ -621,6 +667,8 @@ const syncUnreadMessages = async (wbot) => {
/* eslint-disable no-await-in-loop */ /* eslint-disable no-await-in-loop */
for (const chat of chats) { for (const chat of chats) {
// console.log('chat: ', chat)
if (chat.unreadCount > 0) { if (chat.unreadCount > 0) {
const unreadMessages = await chat.fetchMessages({ const unreadMessages = await chat.fetchMessages({
@ -685,14 +733,13 @@ const getWbotMessage = async (messageId, number, limit,) => {
async function whatsappMonitor(newState, omnihit_url, data) { async function whatsappMonitor(newState, omnihit_url, data) {
dbcc.query("UPDATE Whatsapps SET status = ? where id = ?", [newState, process.env.WHATSAPP_ID], const whatsapp = await whatsappUpdateStatus(newState)
function (err, result) {
if (err)
console.log("ERROR: " + err);
// else if (whatsapp && whatsapp.affectedRows) {
// console.log('myslq result: ', result); console.log('whatsapp status update affectedRows: ', whatsapp.affectedRows)
}); }
// console.log(' whatsappwhatsappwhatsappwhatsapp: ', whatsapp)
try { try {
@ -702,6 +749,23 @@ async function whatsappMonitor(newState, omnihit_url, data) {
} }
} }
async function whatsappUpdateStatus(newState) {
return await new Promise((resolve, reject) => {
dbcc.query("UPDATE Whatsapps SET status = ? where id = ?", [newState, process.env.WHATSAPP_ID],
function (err, result) {
if (err) {
console.log("ERROR: " + err);
reject(err);
}
else {
resolve(result);
}
});
});
}
async function handleMessage(msg) { async function handleMessage(msg) {
console.log('Entrou no message_create'); console.log('Entrou no message_create');
@ -709,7 +773,7 @@ async function handleMessage(msg) {
let msgContact = null; let msgContact = null;
let media = null; let media = null;
if (msg.fromMe) { if (msg.fromMe) {
msgContact = await client.getContactById(msg.to); msgContact = await client.getContactById(msg.to);
@ -740,7 +804,7 @@ async function handleMessage(msg) {
quotedMsg: quotedMsg ? quotedMsg.id.id : null, quotedMsg: quotedMsg ? quotedMsg.id.id : null,
media: media media: media
}; };
socketIo.emit("message_create", data); socketIo.emit("message_create", data);
@ -868,6 +932,14 @@ async function monitor() {
console.log(`WHATSAPP_ID: ${process.env.WHATSAPP_ID} | CLIENT MOBILEUID: ${mobileuid} | NAME: ${process.env.MOBILENAME} | ENV MOBILEUID: ${process.env.MOBILEUID} | STATUS: ${stat}`) console.log(`WHATSAPP_ID: ${process.env.WHATSAPP_ID} | CLIENT MOBILEUID: ${mobileuid} | NAME: ${process.env.MOBILENAME} | ENV MOBILEUID: ${process.env.MOBILEUID} | STATUS: ${stat}`)
if (stat && stat === 'CONNECTED') {
const result = await whatsappUpdateStatus('CONNECTED')
if (result)
console.log(`Update status to CONNECTED WHATSAPP_ID: ${process.env.WHATSAPP_ID} => result.affectedRows: ${result.affectedRows}`)
}
} catch (error) { } catch (error) {
//new Date(new Date() + 'UTC') //new Date(new Date() + 'UTC')

View File

@ -107,6 +107,7 @@ export const store = async (req: Request, res: Response): Promise<Response> => {
number: Yup.string() number: Yup.string()
.required() .required()
.matches(/^\d+$/, "Invalid number format. Only numbers is allowed.") .matches(/^\d+$/, "Invalid number format. Only numbers is allowed.")
.matches(/^55\d+$/, "The number must start with 55.")
}); });
try { try {
@ -173,10 +174,9 @@ export const update = async (
const schema = Yup.object().shape({ const schema = Yup.object().shape({
name: Yup.string(), name: Yup.string(),
number: Yup.string().matches( number: Yup.string()
/^\d+$/, .matches(/^\d+$/,"Invalid number format. Only numbers is allowed.")
"Invalid number format. Only numbers is allowed." .matches(/^55\d+$/, "The number must start with 55.")
)
}); });
try { try {

View File

@ -14,217 +14,139 @@ import SendWhatsAppMessage from "../services/WbotServices/SendWhatsAppMessage";
import { Op, where, Sequelize } from "sequelize"; import { Op, where, Sequelize } from "sequelize";
import ShowTicketServiceByContactId from "../services/TicketServices/ShowTicketServiceByContactId"; import ShowTicketServiceByContactId from "../services/TicketServices/ShowTicketServiceByContactId";
import hitPortalMonitoring from "../helpers/HitPortalMonitoring"; import hitPortalMonitoring from "../helpers/HitPortalMonitoring";
import { sendDialogflowAwswer, verifyContact } from "../services/WbotServices/wbotMessageListener";
import CheckIsValidContact from "../services/WbotServices/CheckIsValidContact";
import Contact from "../models/Contact";
import GetProfilePicUrl from "../services/WbotServices/GetProfilePicUrl";
import CreateContactService from "../services/ContactServices/CreateContactService";
import { resourceUsage } from "process";
import FindOrCreateTicketServiceBot from "../services/TicketServices/FindOrCreateTicketServiceBot";
// type IndexQuery = { // type IndexQuery = {
// centro_custo: string; // cod_web: string;
// }; // };
export const hit = async (req: Request, res: Response): Promise<Response> => { export const hit = async (req: Request, res: Response): Promise<Response> => {
// const { // const {
// centro_custo, // cod_web,
// } = req.body as IndexQuery; // } = req.body as IndexQuery;
console.log('req.boy: ', req.body) console.log('req.boy: ', req.body)
console.log('req.boy: ', req.body['centro_custo']) console.log("req.body['cod_web']: ", req.body['cod_web'])
console.log('ACTION: ', req.body['action']) console.log("req.body['action']: ", req.body['action'])
console.log("req.body['phone']: ", req.body['phone'])
console.log("req.body['name']: ", req.body['name'])
if (req.headers["auth"] === '0424bd59b807674191e7d77572075f33') { if (req.headers["auth"] === '0424bd59b807674191e7d77572075f33') {
let contact = null let phone = req.body['phone']
let name = req.body['name']
try { if (!phone) return res.status(200).json({ "message": "Ok" });
contact = await ContactByCustomField(req.body['centro_custo']) const regex = /^55/;
} catch (error) { phone = phone.match(/\d+/g).join('');
console.log('There was an error on try get centro_custo info: ', error) if (!regex.test(phone)) {
phone = '55' + phone;
}
if (phone.length < 7) return res.status(200).json({ "message": "Ok" });
let validNumber = await CheckIsValidContact(phone);
if (!validNumber) {
return res.status(200).json({ "message": "Ok" });
}
validNumber = req.body['cod_web']==='0001' ? validNumber : '5517988325936'
let contact = await Contact.findOne({ where: { number: validNumber } });
// let contact = await Contact.findOne({ where: { number: '5517988325936' } });
if (!contact) {
const profilePicUrl = await GetProfilePicUrl(validNumber);
contact = await CreateContactService({
name: name || phone,
number: validNumber,
profilePicUrl: profilePicUrl,
});
const io = getIO();
io.emit("contact", {
action: "create",
contact
});
}
let response: any = await hitPortalMonitoring({
'params[n_chamado_web]': req.body['n_chamado_web'],
'method': 'omnihit.consultachamado',
})
if (!response || response.length == 0 || !contact) {
console.log('Empty result from hit portal monitoring to n_chamado_web: ', req.body['n_chamado_web'])
return res.status(200).json({ "message": "Ok" });
}
const botInfo = await BotIsOnQueue('botqueue')
let ticket: any = await ShowTicketServiceByContactId(contact.id)
if (!ticket.dataValues.id) {
const defaultWhatsapp = await GetDefaultWhatsApp();
let ticket_obj: any = await FindOrCreateTicketServiceBot(
contact,
defaultWhatsapp.id!,
0,
);
ticket = ticket_obj.ticket
} }
if (req.body['action'] === 'atdfechou') { if (ticket.id && ticket.status == 'pending') {
console.log('FECHOU') await sendMessageHitMonitoring(response, ticket);
try {
console.log('atdfechou ----------> THE CONTACT: ', contact)
if (contact) {
let data = req.body
let str = ''
let str2 = ''
// const exclude = ["action", "centro_custo", "n_chamado_web", "historico"];
// for (const key in data) {
// if (exclude.includes(`${key}`)) {
// continue
// }
// str += `${key.replace('hostname', 'cliente')}: ${data[key]}\n`
// }
str = `*Cliente*: ${contact['contact.name']}`
let historico = data['historico']
for (const key in historico) {
const hist = Object.keys(historico[key]);
hist.forEach((keys, index) => {
str2 += `*${keys}*: ${historico[key][keys]}\n`
});
str2 += '\n'
}
console.log('--------------> str: ', str)
console.log('--------------> str2: ', str2)
const botInfo = await BotIsOnQueue('botqueue')
let ticket = await ShowTicketServiceByContactId(contact['contact.id'])
if (ticket.id && ticket.status == 'pending') {
await sendMessageHitMonitoring(`*Olá. Somos a TI Espaçolaser.*\nO chamado da sua loja ${contact['contact.name']} foi fechado pela operadora. Abaixo seguem informações sobre o incidente.\n\n*Situação do chamado na Operadora*\n\n*Incidente:*\n\n ${str}\n\n*Atualizações*:\n\n${str2}`, ticket);
}
else if (!ticket.id) {
ticket = await CreateTicketService({ contactId: contact['contact.id'], status: 'open', userId: botInfo.userIdBot });
console.log('botInfo.botQueueId: ', botInfo.botQueueId)
await UpdateTicketService({ ticketData: { queueId: botInfo.botQueueId }, ticketId: ticket.id });
await sendMessageHitMonitoring(`*Olá. Somos a TI Espaçolaser.*\nO chamado da sua loja ${contact['contact.name']} foi fechado pela operadora. Abaixo seguem informações sobre o incidente.\n\n*Situação do chamado na Operadora*\n\n*Incidente:*\n\n ${str}\n\n*Atualizações*:\n\n${str2}`, ticket);
}
}
} catch (error) {
console.log(`Error on try sending the monitor message closed: `, error)
}
}
else if (req.body['action'] === 'atdatualizou') {
console.log('status: atdatualizou --------------> contact: ', contact)
if (contact) {
try {
let response = await hitPortalMonitoring(req.body['centro_custo'])
if (!response || response.length == 0) {
console.log('Empty result from hit portal monitoring. Centro_de_custo: ', req.body['centro_custo'])
return res.status(200).json({ "message": "Ok" });
}
const botInfo = await BotIsOnQueue('botqueue')
let ticket = await ShowTicketServiceByContactId(contact['contact.id'])
if (ticket.id && ticket.status == 'pending') {
await sendMessageHitMonitoring(`*Olá. Somos a TI Espaçolaser.*\nAtualização do chamado para sua loja ${contact['contact.name']}. Abaixo seguem informações sobre o incidente para que possam acompanhar.\n\n*Situação do chamado na Operadora*\n\n*Incidente*:\n\n ${response[0].header}\n${response[0].body}`, ticket);
}
else if (!ticket.id) {
ticket = await CreateTicketService({ contactId: contact['contact.id'], status: 'open', userId: botInfo.userIdBot });
console.log('botInfo.botQueueId: ', botInfo.botQueueId)
await UpdateTicketService({ ticketData: { queueId: botInfo.botQueueId }, ticketId: ticket.id });
await sendMessageHitMonitoring(`*Olá. Somos a TI Espaçolaser.*\nAtualização do chamado para sua loja ${contact['contact.name']}. Abaixo seguem informações sobre o incidente para que possam acompanhar.\n\n*Situação do chamado na Operadora*\n\n*Incidente*:\n\n ${response[0].header}\n${response[0].body}`, ticket);
}
} catch (error) {
console.log(`Error on try sending the message monitor: `, error)
}
}
} }
else { else if (ticket.id && ticket.userId == botInfo.userIdBot) {
console.log('status: atdatabriu --------------> contact: ', contact) let queue = await ShowQueueService(botInfo.botQueueId);
if (contact) { await UpdateTicketService({ ticketData: { queueId: queue.id }, ticketId: ticket.id });
try { ticket = await ShowTicketService(ticket.id);
let response = await hitPortalMonitoring(req.body['centro_custo']) let msg: any = { type: 'chat', from: `${contact.number}@c.us`, body: '0' }
if (!response || response.length == 0) { await sendDialogflowAwswer(ticket.whatsappId, ticket, msg, contact, false);
console.log('Empty result from hit portal monitoring. Centro_de_custo: ', req.body['centro_custo'])
return res.status(200).json({ "message": "Ok" });
}
const botInfo = await BotIsOnQueue('botqueue') await sendMessageHitMonitoring(response, ticket);
let ticket = await ShowTicketServiceByContactId(contact['contact.id'])
if (ticket.id && ticket.status == 'pending') {
await sendMessageHitMonitoring(`*Olá. Somos a TI Espaçolaser.*\nIdentificamos em nossos monitoramentos que há um problema na internet da sua loja ${contact['contact.name']} e já estamos resolvendo. Abaixo seguem informações sobre o incidente para que possam acompanhar.\n\n*Situação do chamado na Operadora*\n\n*Incidente*:\n\n ${response[0].header}\n${response[0].body}`, ticket);
}
else if (!ticket.id) {
ticket = await CreateTicketService({ contactId: contact['contact.id'], status: 'open', userId: botInfo.userIdBot });
console.log('botInfo.botQueueId: ', botInfo.botQueueId)
await UpdateTicketService({ ticketData: { queueId: botInfo.botQueueId }, ticketId: ticket.id });
await sendMessageHitMonitoring(`*Olá. Somos a TI Espaçolaser.*\nIdentificamos em nossos monitoramentos que há um problema na internet da sua loja ${contact['contact.name']} e já estamos resolvendo. Abaixo seguem informações sobre o incidente para que possam acompanhar.\n\n*Situação do chamado na Operadora*\n\n*Incidente*:\n\n ${response[0].header}\n${response[0].body}`, ticket);
}
} catch (error) {
console.log(`Error on try sending the message monitor: `, error)
}
}
} }
} }
else { else {
res.status(401).json({ "message": "Token Inválido!" }); return res.status(401).json({ "message": "Token Inválido!" });
} }
return res.status(200).json({ "message": "Ok" }); return res.status(200).json({ "message": "Ok" });
}; };
async function sendMessageHitMonitoring(msg: string, ticket: Ticket) { async function sendMessageHitMonitoring(msg: any, ticket: Ticket) {
if (msg && msg.length > 0) { if (msg && msg.length > 0) {

View File

@ -9,6 +9,7 @@ import ShowTicketService from "../services/TicketServices/ShowTicketService";
import DeleteWhatsAppMessage from "../services/WbotServices/DeleteWhatsAppMessage"; import DeleteWhatsAppMessage from "../services/WbotServices/DeleteWhatsAppMessage";
import SendWhatsAppMedia from "../services/WbotServices/SendWhatsAppMedia"; import SendWhatsAppMedia from "../services/WbotServices/SendWhatsAppMedia";
import SendWhatsAppMessage from "../services/WbotServices/SendWhatsAppMessage"; import SendWhatsAppMessage from "../services/WbotServices/SendWhatsAppMessage";
type IndexQuery = { type IndexQuery = {
pageNumber: string; pageNumber: string;
@ -19,12 +20,15 @@ type MessageData = {
fromMe: boolean; fromMe: boolean;
read: boolean; read: boolean;
quotedMsg?: Message; quotedMsg?: Message;
id?: string;
}; };
export const index = async (req: Request, res: Response): Promise<Response> => { export const index = async (req: Request, res: Response): Promise<Response> => {
const { ticketId } = req.params; const { ticketId } = req.params;
const { pageNumber } = req.query as IndexQuery; const { pageNumber } = req.query as IndexQuery;
// console.log(':::::::::::::> TICKET ID: ', ticketId)
const { count, messages, ticket, hasMore } = await ListMessagesService({ const { count, messages, ticket, hasMore } = await ListMessagesService({
pageNumber, pageNumber,
ticketId ticketId
@ -34,24 +38,23 @@ export const index = async (req: Request, res: Response): Promise<Response> => {
return res.json({ count, messages, ticket, hasMore }); return res.json({ count, messages, ticket, hasMore });
}; };
export const store = async (req: Request, res: Response): Promise<Response> => { export const store = async (req: Request, res: Response): Promise<Response> => {
const { ticketId } = req.params; const { ticketId } = req.params;
const { body, quotedMsg }: MessageData = req.body; const { body, quotedMsg }: MessageData = req.body;
const medias = req.files as Express.Multer.File[]; const medias = req.files as Express.Multer.File[];
const ticket = await ShowTicketService(ticketId); const ticket = await ShowTicketService(ticketId);
console.log('TICKET ID: ', ticketId) console.log("TICKET ID: ", ticketId);
// SetTicketMessagesAsRead(ticket); // SetTicketMessagesAsRead(ticket);
if (medias) { if (medias) {
await Promise.all( await Promise.all(
medias.map(async (media: Express.Multer.File) => { medias.map(async (media: Express.Multer.File) => {
console.log(
console.log(`\n >>>>>>>>>> SENDING MESSAGE MEDIA `\n >>>>>>>>>> SENDING MESSAGE MEDIA
Parcial ticket info and media: Parcial ticket info and media:
ticket.id: ${ticket.id} ticket.id: ${ticket.id}
ticket.status: ${ticket.status} ticket.status: ${ticket.status}
@ -61,13 +64,15 @@ export const store = async (req: Request, res: Response): Promise<Response> => {
ticket.contact.profilePicUrl: ${ticket.contact.profilePicUrl} ticket.contact.profilePicUrl: ${ticket.contact.profilePicUrl}
ticket.user.id: ${ticket.user.id} ticket.user.id: ${ticket.user.id}
ticket.user.name: ${ticket.user.name} ticket.user.name: ${ticket.user.name}
media:`, media,'\n') media:`,
media,
"\n"
);
await SendWhatsAppMedia({ media, ticket }); await SendWhatsAppMedia({ media, ticket });
}) })
); );
} else { } else {
console.log(`\n >>>>>>>>>> SENDING MESSAGE console.log(`\n >>>>>>>>>> SENDING MESSAGE
Parcial ticket info: Parcial ticket info:
ticket.id: ${ticket.id} ticket.id: ${ticket.id}
@ -78,8 +83,7 @@ export const store = async (req: Request, res: Response): Promise<Response> => {
ticket.contact.name: ${ticket.contact.name} ticket.contact.name: ${ticket.contact.name}
ticket.contact.profilePicUrl: ${ticket.contact.profilePicUrl} ticket.contact.profilePicUrl: ${ticket.contact.profilePicUrl}
ticket.user.id: ${ticket.user.id} ticket.user.id: ${ticket.user.id}
ticket.user.name: ${ticket.user.name}\n`) ticket.user.name: ${ticket.user.name}\n`);
await SendWhatsAppMessage({ body, ticket, quotedMsg }); await SendWhatsAppMessage({ body, ticket, quotedMsg });
} }
@ -102,4 +106,4 @@ export const remove = async (
}); });
return res.send(); return res.send();
}; };

View File

@ -19,9 +19,10 @@ import ptBR from 'date-fns/locale/pt-BR';
import { splitDateTime } from "../helpers/SplitDateTime"; import { splitDateTime } from "../helpers/SplitDateTime";
import format from 'date-fns/format'; import format from 'date-fns/format';
import ListTicketsServiceCache from "../services/TicketServices/ListTicketServiceCache"; import ListTicketsServiceCache from "../services/TicketServices/ListTicketServiceCache";
import { searchTicketCache, loadTicketsCache, } from '../helpers/TicketCache' import { searchTicketCache, loadTicketsCache, } from '../helpers/TicketCache'
import { Op } from "sequelize";
@ -34,6 +35,7 @@ type IndexQuery = {
withUnreadMessages: string; withUnreadMessages: string;
queueIds: string; queueIds: string;
unlimited?: string; unlimited?: string;
searchParamContent?: string
}; };
interface TicketData { interface TicketData {
@ -41,6 +43,10 @@ interface TicketData {
status: string; status: string;
queueId: number; queueId: number;
userId: number; userId: number;
whatsappId?: string | number
msg?: string,
transfer?: boolean | undefined,
fromMe?: boolean
} }
@ -51,6 +57,16 @@ import TicketEmiterSumOpenClosedByUser from "../helpers/OnlineReporEmiterInfoByU
import CountTicketService from "../services/TicketServices/CountTicketService"; import CountTicketService from "../services/TicketServices/CountTicketService";
import CountTicketsByUserQueue from "../services/UserServices/CountTicketsByUserQueue"; import CountTicketsByUserQueue from "../services/UserServices/CountTicketsByUserQueue";
import ShowUserService from "../services/UserServices/ShowUserService"; import ShowUserService from "../services/UserServices/ShowUserService";
import axios from "axios";
import User from "../models/User";
import CheckContactOpenTickets from "../helpers/CheckContactOpenTickets";
import QueuesByUser from "../services/UserServices/ShowQueuesByUser";
import GetDefaultWhatsApp from "../helpers/GetDefaultWhatsApp";
import { getWbot } from "../libs/wbot";
import endPointQuery from "../helpers/old_EndPointQuery";
import Contact from "../models/Contact";
import BotIsOnQueue from "../helpers/BotIsOnQueue";
import { setMessageAsRead } from "../helpers/SetMessageAsRead";
export const index = async (req: Request, res: Response): Promise<Response> => { export const index = async (req: Request, res: Response): Promise<Response> => {
@ -62,7 +78,8 @@ export const index = async (req: Request, res: Response): Promise<Response> => {
showAll, showAll,
queueIds: queueIdsStringified, queueIds: queueIdsStringified,
withUnreadMessages, withUnreadMessages,
unlimited unlimited,
searchParamContent
} = req.query as IndexQuery; } = req.query as IndexQuery;
@ -83,32 +100,41 @@ export const index = async (req: Request, res: Response): Promise<Response> => {
userId, userId,
queueIds, queueIds,
withUnreadMessages, withUnreadMessages,
unlimited unlimited,
searchParamContent
}); });
return res.status(200).json({ tickets, count, hasMore }); return res.status(200).json({ tickets, count, hasMore });
}; };
export const store = async (req: Request, res: Response): Promise<Response> => { export const store = async (req: Request, res: Response): Promise<Response> => {
const { contactId, status, userId }: TicketData = req.body; const { contactId, status, userId, msg, queueId }: TicketData = req.body;
// test del const botInfo = await BotIsOnQueue('botqueue')
let ticket = await Ticket.findOne({ where: { contactId, status: 'queueChoice' } });
let ticket = await Ticket.findOne({
where: {
[Op.or]: [
{ contactId, status: 'queueChoice' },
{ contactId, status: 'open', userId: botInfo.userIdBot }
]
}
});
if (ticket) { if (ticket) {
await UpdateTicketService({ ticketData: { status: 'open', userId: userId, }, ticketId: ticket.id }); await UpdateTicketService({ ticketData: { status: 'open', userId: userId, queueId }, ticketId: ticket.id });
} }
else { else {
ticket = await CreateTicketService({ contactId, status, userId }); ticket = await CreateTicketService({ contactId, status, userId, queueId });
} }
const io = getIO(); const io = getIO();
io.to(ticket.status).emit("ticket", { io.to(ticket.status).emit("ticket", {
action: "update", action: "update",
ticket ticket
}); });
//
// const ticket = await CreateTicketService({ contactId, status, userId }); // const ticket = await CreateTicketService({ contactId, status, userId });
@ -138,7 +164,6 @@ export const show = async (req: Request, res: Response): Promise<Response> => {
return res.status(200).json({ contact, statusChatEnd, schedulesContact }); return res.status(200).json({ contact, statusChatEnd, schedulesContact });
}; };
export const count = async (req: Request, res: Response): Promise<Response> => { export const count = async (req: Request, res: Response): Promise<Response> => {
// type indexQ = { status: string; date?: string; }; // type indexQ = { status: string; date?: string; };
@ -149,11 +174,11 @@ export const count = async (req: Request, res: Response): Promise<Response> => {
return res.status(200).json(ticketCount); return res.status(200).json(ticketCount);
}; };
export const update = async (req: Request, res: Response): Promise<Response> => { export const update = async (req: Request, res: Response): Promise<Response> => {
console.log('ENTROU NO UPDATE TICKET CONTROLLER')
const { ticketId } = req.params; const { ticketId } = req.params;
const userOldInfo = await Ticket.findByPk(ticketId) const userOldInfo = await Ticket.findByPk(ticketId)
@ -175,10 +200,6 @@ export const update = async (req: Request, res: Response): Promise<Response> =>
}); });
///////////////////////////////
//
if (scheduleData.farewellMessage) { if (scheduleData.farewellMessage) {
const whatsapp = await ShowWhatsAppService(ticket.whatsappId); const whatsapp = await ShowWhatsAppService(ticket.whatsappId);
@ -188,11 +209,11 @@ export const update = async (req: Request, res: Response): Promise<Response> =>
await SendWhatsAppMessage({ body: farewellMessage, ticket }); await SendWhatsAppMessage({ body: farewellMessage, ticket });
} }
} }
// lembrete // agendamento // lembrete // agendamento
if (scheduleData.statusChatEndId === '2' || scheduleData.statusChatEndId === '3') { if (scheduleData.statusChatEndId === '2' || scheduleData.statusChatEndId === '3') {
if (isScheduling(scheduleData.schedulingDate, scheduleData.schedulingTime)) { if (isScheduling(scheduleData.schedulingDate, scheduleData.schedulingTime)) {
console.log('*** É AGENDAMENTO!') console.log('*** É AGENDAMENTO!')
@ -217,15 +238,52 @@ export const update = async (req: Request, res: Response): Promise<Response> =>
} }
else { else {
const ticketData: TicketData = req.body; // Para aparecer pendente para todos usuarios que estao na fila
if (req.body.transfer) {
req.body.userId = null
}
let ticketData: TicketData = req.body;
// console.log('ticketData: ', ticketData)
// console.log('ticketData.transfer', ticketData.transfer)
// return res.send()
// if (ticketData.transfer) {
// const defaultWhatsapp: any = await GetDefaultWhatsApp(ticketData.userId);
// const _ticket: any = await Ticket.findByPk(ticketId)
// if (defaultWhatsapp && ticketData.status != 'open') {
// await CheckContactOpenTickets(_ticket.dataValues.contactId, defaultWhatsapp.dataValues.id)
// }
// ticketData.whatsappId = defaultWhatsapp.dataValues.id
// }
console.log('--------> ticketData.status: ', ticketData.status, ' | ticketData.fromMe: ', ticketData.fromMe)
//ticketData: { status: 'open', userId: 4 } , ticketId
const { ticket } = await UpdateTicketService({ const { ticket } = await UpdateTicketService({
ticketData, ticketData,
ticketId ticketId,
}); });
if (ticketData.status == 'open' && !ticketData.fromMe) {
await setMessageAsRead(ticket);
}
console.log('ticket.unreadMessages: ', ticket.unreadMessages)
if (ticketData.userId) { if (ticketData.userId) {
const dateToday = splitDateTime(new Date(format(new Date(), 'yyyy-MM-dd HH:mm:ss', { locale: ptBR }))) const dateToday = splitDateTime(new Date(format(new Date(), 'yyyy-MM-dd HH:mm:ss', { locale: ptBR })))
@ -244,7 +302,7 @@ export const update = async (req: Request, res: Response): Promise<Response> =>
const dateToday = splitDateTime(new Date(format(new Date(), 'yyyy-MM-dd HH:mm:ss', { locale: ptBR }))) const dateToday = splitDateTime(new Date(format(new Date(), 'yyyy-MM-dd HH:mm:ss', { locale: ptBR })))
if (userOldInfo.userId) { if (userOldInfo.userId) {
TicketEmiterSumOpenClosedByUser(userOldInfo.userId.toString(), dateToday.fullDate, dateToday.fullDate) TicketEmiterSumOpenClosedByUser(userOldInfo.userId.toString(), dateToday.fullDate, dateToday.fullDate)
@ -258,7 +316,8 @@ export const update = async (req: Request, res: Response): Promise<Response> =>
}; };
// export const update = async ( // export const update = async (
// req: Request, // req: Request,
@ -305,3 +364,11 @@ export const remove = async (
return res.status(200).json({ message: "ticket deleted" }); return res.status(200).json({ message: "ticket deleted" });
}; };
// export async function setMessageAsRead(ticket: Ticket) {
// const wbot_url = await getWbot(ticket.whatsappId);
// console.log('wbot_url: ', wbot_url, ' | ticket.contact.number: ', ticket.contact.number);
// await endPointQuery(`${wbot_url}/api/sendSeen`, { number: `${ticket.contact.number}@${ticket.isGroup ? "g" : "c"}.us` });
// }

View File

@ -14,9 +14,7 @@ export const wbotMonitorRemote = async (req: Request, res: Response): Promise<Re
const { action, whatsappId, reason } = req.body const { action, whatsappId, reason } = req.body
console.log('-----------> ACTION: ', req.body['action']) console.log('-----------> ACTION: ', req.body['action'])
// let whatsapp = await ShowWhatsAppService(whatsappId)
const whatsapp: any = await Whatsapp.findByPk(whatsappId, { raw: true }) const whatsapp: any = await Whatsapp.findByPk(whatsappId, { raw: true })

View File

@ -3,7 +3,10 @@ const fs = require('fs')
import axios from 'axios'; import axios from 'axios';
import * as https from "https"; import * as https from "https";
const endPointQuery = async (url: string, method: string, param: string = '') => { const endPointQuery = async (
url: string,
method: string,
params: object) => {
let response: any = null let response: any = null
@ -26,14 +29,18 @@ const endPointQuery = async (url: string, method: string, param: string = '') =>
// const url = 'http://177.107.193.124:8095/labs/zabbix-frontend/api/api.php' // const url = 'http://177.107.193.124:8095/labs/zabbix-frontend/api/api.php'
let payload = {
response = await axios.post(url, {
'auth': '0424bd59b807674191e7d77572075f33', 'auth': '0424bd59b807674191e7d77572075f33',
'jsonrpc': '2.0', 'jsonrpc': '2.0',
'method': 'chamado.ematendimento', // 'method': 'omnihit.consultachamado',
'params[ccusto]': param,
id: '101' id: '101'
}, { }
payload = { ...payload, ...params }
console.log('xxxxxx payload: ', payload)
response = await axios.post(url, payload, {
httpsAgent, httpsAgent,
headers: { 'Content-Type': 'multipart/form-data' }, headers: { 'Content-Type': 'multipart/form-data' },
}); });
@ -44,6 +51,8 @@ const endPointQuery = async (url: string, method: string, param: string = '') =>
console.error(`Erro ao consultar endpoint ${url}: ${error}`); console.error(`Erro ao consultar endpoint ${url}: ${error}`);
} }
// console.log('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> response: ', response)
return response return response
} }

View File

@ -6,62 +6,19 @@ import endPointQuery from "./EndpointQuery";
import WhatsQueueIndex from "./WhatsQueueIndex"; import WhatsQueueIndex from "./WhatsQueueIndex";
const hitPortalMonitoring = async (centro_de_custo: string) => { const hitPortalMonitoring = async (params: object) => {
let msg_endpoint: any = [] let response = await endPointQuery('http://177.107.192.247:8095/labs/monitoramentohit/api/api.php', 'post', params)
let response2 = await endPointQuery('http://177.107.193.124:8095/labs/zabbix-frontend/api/api.php', 'post', centro_de_custo.trim())
if (response2 && response2.data.result) { if (response && response.data.result.trim().length > 0) {
return response.data.result
response2 = response2.data.result; }
else if (response && response.data.result.trim().length === 0) {
for (let i = 0; i < response2.length; i++) { return ''
}
let data = '' else {
let sub_data = '*Atualizações:*\n\n' return null
}
const properties: any = Object.entries(response2[i]);
for (let x = 0; x < properties.length; x++) {
if (typeof (properties[x][1]) != 'object') {
data += `*${properties[x][0]}*: ${properties[x][1].replace(/(\r\n|\n|\r)/gm, "")}\n`
}
else if (typeof (properties[x][1]) == 'object') {
const sub_properties = properties[x][1];
for (let k = 0; k < sub_properties.length; k++) {
const inner_properties: any = Object.entries(sub_properties[k]);
for (let y = 0; y < inner_properties.length; y++) {
sub_data += `*${inner_properties[y][0]}*: ${inner_properties[y][1].replace(/(\r\n|\n|\r)/gm, "")}\n`
}
sub_data += '\n'
}
}
}
msg_endpoint.push({ header: data, body: sub_data })
}
}
else {
msg_endpoint = null
}
return msg_endpoint
} }

View File

@ -0,0 +1,58 @@
import { subSeconds } from "date-fns";
import Message from "../models/Message";
import { Op, Sequelize } from "sequelize";
const mostRepeatedPhrase = async (ticketId: number | string, fromMe: boolean = false) => {
let res: any = { body: '', occurrences: 0 }
try {
const mostRepeatedPhrase: any = await Message.findOne({
where: {
ticketId: ticketId,
fromMe: fromMe ? fromMe : 0,
body: {
[Op.notRegexp]: '^[0-9]+$',
},
updatedAt: {
[Op.between]: [+subSeconds(new Date(), 150), +new Date()],
},
},
attributes: [
'body',
[Sequelize.fn('COUNT', Sequelize.col('body')), 'occurrences'],
],
group: ['body'],
order: [[Sequelize.literal('occurrences'), 'DESC']],
limit: 1,
});
if (mostRepeatedPhrase) {
const { body, occurrences } = mostRepeatedPhrase.get();
console.log(`The most repeated phrase is "${body}" with ${occurrences} occurrences.`);
const isNumber = /^\d+$/.test(body.trim());
if (!isNumber) {
return { body, occurrences }
}
} else {
console.log('No phrases found.');
}
} catch (error) {
console.log('error on MostRepeatedPhrase: ', error)
}
return { body: '', occurrences: 0 }
}
export default mostRepeatedPhrase;

View File

@ -10,6 +10,9 @@ import path from "path";
import { convertBytes } from "./ConvertBytes"; import { convertBytes } from "./ConvertBytes";
import { deleteScheduleByTicketIdCache } from "./SchedulingNotifyCache"; import { deleteScheduleByTicketIdCache } from "./SchedulingNotifyCache";
import SchedulingNotify from "../models/SchedulingNotify"; import SchedulingNotify from "../models/SchedulingNotify";
import Ticket from "../models/Ticket";
import { Sequelize, Op } from "sequelize";
const fastFolderSize = require('fast-folder-size') const fastFolderSize = require('fast-folder-size')
const { promisify } = require('util') const { promisify } = require('util')
@ -41,33 +44,39 @@ const monitor = async () => {
try { try {
const { schedulingNotifies, count, hasMore } = await ListSchedulingNotifyService({ searchParam: dateParm, pageNumber: "1" }); const { schedulingNotifies, count, hasMore } = await ListSchedulingNotifyService({ searchParam: dateParm, pageNumber: "1" });
if (schedulingNotifies && schedulingNotifies.length > 0) {
for (let i = 0; i < schedulingNotifies.length; i++) { if (schedulingNotifies && schedulingNotifies.length > 0) {
for (let i = 0; i < schedulingNotifies.length; i++) {
const ticket: any = await ShowTicketService(+schedulingNotifies[i].ticketId);
let _ticket = await Ticket.findOne({
where: {
contactId: ticket.contactId,
status: { [Op.in]: ['open', 'pending'] }
}
})
await deleteScheduleByTicketIdCache(schedulingNotifies[i].ticketId)
await DeleteSchedulingNotifyService(schedulingNotifies[i].id)
if (_ticket) continue
if (ticket.dataValues.status == 'closed') {
await ticket.update({ status: 'pending' })
}
const ticket = await ShowTicketService(+schedulingNotifies[i].ticketId);
await new Promise(f => setTimeout(f, 3000)); await new Promise(f => setTimeout(f, 3000));
if(!ticket.queue){
await ticket.update({status: 'open'})
}
// SetTicketMessagesAsRead(ticket);
await SendWhatsAppMessage({ await SendWhatsAppMessage({
body: schedulingNotifies[i].message, ticket body: schedulingNotifies[i].message, ticket
}); });
await deleteScheduleByTicketIdCache(schedulingNotifies[i].ticketId)
await DeleteSchedulingNotifyService(schedulingNotifies[i].id)
} }
} }
@ -173,7 +182,7 @@ _fifo = setInterval(SchedulingNotifySendMessage, 5000);
module.exports = SchedulingNotifySendMessage module.exports = SchedulingNotifySendMessage

View File

@ -14,19 +14,7 @@ function sendWhatsAppMessageSocket(ticket: Ticket, body: string, quotedMsgSerial
quotedMessageId: quotedMsgSerializedId, quotedMessageId: quotedMsgSerializedId,
linkPreview: false linkPreview: false
} }
}); });
// io.emit("send_message", {
// action: "create",
// msg: {
// number: `${ticket.contact.number}@${ticket.isGroup ? "g" : "c"}.us`,
// body: body,
// quotedMessageId: quotedMsgSerializedId,
// linkPreview: false
// }
// });
} }

View File

@ -0,0 +1,12 @@
import { getWbot } from "../libs/wbot";
import Ticket from "../models/Ticket";
import endPointQuery from "./old_EndPointQuery";
export async function setMessageAsRead(ticket: Ticket) {
const wbot_url = await getWbot(ticket.whatsappId);
console.log('from wbotMessagelistener wbot_url: ', wbot_url, ' | ticket.contact.number: ', ticket.contact.number);
await endPointQuery(`${wbot_url}/api/sendSeen`, { number: `${ticket.contact.number}@${ticket.isGroup ? "g" : "c"}.us` });
}

View File

@ -45,13 +45,7 @@ export const initIO = (httpServer: Server): SocketIO => {
io.on("connection", socket => { io.on("connection", socket => {
logger.info("Client Connected"); logger.info("Client Connected");
socket.on("joinWhatsSession", (whatsappId: string) => { socket.on("joinWhatsSession", (whatsappId: string) => {
logger.info(`A client joined a joinWhatsSession channel: ${whatsappId}`); logger.info(`A client joined a joinWhatsSession channel: ${whatsappId}`);
@ -65,6 +59,8 @@ export const initIO = (httpServer: Server): SocketIO => {
socket.on("message_create", async (data: any) => { socket.on("message_create", async (data: any) => {
// console.log('data: ', data)
handleMessage(data.msg, data); handleMessage(data.msg, data);
}); });

View File

@ -4,6 +4,6 @@ import * as HitController from "../controllers/HitController";
const hitRoutes = express.Router(); const hitRoutes = express.Router();
hitRoutes.post("/espacolaser/incidente", HitController.hit); hitRoutes.post("/omnihit/incidente", HitController.hit);
export default hitRoutes; export default hitRoutes;

View File

@ -13,6 +13,8 @@ messageRoutes.get("/messages/:ticketId", isAuth, MessageController.index);
messageRoutes.post("/messages/:ticketId", isAuth, upload.array("medias"), MessageController.store); messageRoutes.post("/messages/:ticketId", isAuth, upload.array("medias"), MessageController.store);
// messageRoutes.put("/messages/:ticketId", isAuth, MessageController.update)
messageRoutes.delete("/messages/:messageId", isAuth, MessageController.remove); messageRoutes.delete("/messages/:messageId", isAuth, MessageController.remove);
export default messageRoutes; export default messageRoutes;

View File

@ -21,18 +21,23 @@ let flatten = require('flat')
interface Request { interface Request {
contactId: number; contactId: number;
status: string; status: string;
userId: number; userId: number;
queueId?: number | undefined;
} }
const CreateTicketService = async ({ const CreateTicketService = async ({
contactId, contactId,
status, status,
userId userId,
queueId = undefined
}: Request): Promise<Ticket> => { }: Request): Promise<Ticket> => {
console.log('========> queueId: ', queueId)
try { try {
const defaultWhatsapp = await GetDefaultWhatsApp(userId); const defaultWhatsapp = await GetDefaultWhatsApp(userId);
await CheckContactOpenTickets(contactId); await CheckContactOpenTickets(contactId);
@ -42,7 +47,8 @@ const CreateTicketService = async ({
contactId, contactId,
status, status,
isGroup, isGroup,
userId userId,
queueId
}); });
const ticket = await Ticket.findByPk(id, { include: ["contact"] }); const ticket = await Ticket.findByPk(id, { include: ["contact"] });

View File

@ -0,0 +1,147 @@
import { subHours, subMinutes, subSeconds } from "date-fns";
import { Op } from "sequelize";
import BotIsOnQueue from "../../helpers/BotIsOnQueue";
import Contact from "../../models/Contact";
import Ticket from "../../models/Ticket";
import ShowWhatsAppService from "../WhatsappService/ShowWhatsAppService";
import ShowTicketService from "./ShowTicketService";
import AppError from "../../errors/AppError";
import { userInfo } from "os";
import { sendDialogflowAwswer } from "../WbotServices/wbotMessageListener";
import ShowQueueService from "../QueueService/ShowQueueService";
import UpdateTicketService from "./UpdateTicketService";
const FindOrCreateTicketServiceBot = async (
contact: Contact,
whatsappId: number,
unreadMessages: number,
groupContact?: Contact
): Promise<any> => {
try {
let ticket = await Ticket.findOne({
where: {
status: {
[Op.or]: ["open", "pending", "queueChoice"]
},
contactId: groupContact ? groupContact.id : contact.id
}
});
const { queues, greetingMessage } = await ShowWhatsAppService(whatsappId);
//Habilitar esse caso queira usar o bot
const botInfo = await BotIsOnQueue('botqueue')
// const botInfo = { isOnQueue: false }
if (ticket) {
await ticket.update({ unreadMessages });
}
// if (!ticket && groupContact) {
// ticket = await Ticket.findOne({
// where: {
// contactId: groupContact.id
// },
// order: [["updatedAt", "DESC"]]
// });
// if (ticket) {
// await ticket.update({
// status: "pending",
// userId: null,
// unreadMessages
// });
// }
// }
if (!ticket && !groupContact) {
console.log('BOT CREATING OR REOPENING THE TICKET')
ticket = await Ticket.findOne({
where: {
contactId: contact.id,
userId: botInfo.userIdBot
},
order: [["updatedAt", "DESC"]]
});
if (ticket) {
await ticket.update({
status: "open",
userId: botInfo.userIdBot,
unreadMessages
});
console.log('lxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')
await dialogFlowStartContext(contact, ticket, botInfo);
}
}
let created = false
if (!ticket) {
created = true
let status = "open"
if (queues.length > 1 && !botInfo.isOnQueue) {
status = "queueChoice"
}
ticket = await Ticket.create({
contactId: groupContact ? groupContact.id : contact.id,
status: status,
userId: botInfo.userIdBot,
isGroup: !!groupContact,
unreadMessages,
whatsappId
});
console.log('yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy')
await dialogFlowStartContext(contact, ticket, botInfo);
}
ticket = await ShowTicketService(ticket.id);
return { ticket, created };
} catch (error: any) {
console.error('===> Error on FindOrCreateTicketServiceBot.ts file: \n', error)
throw new AppError(error.message);
}
};
export default FindOrCreateTicketServiceBot;
async function dialogFlowStartContext(contact: Contact, ticket: Ticket, botInfo: any) {
let msg: any = { type: 'chat', from: `${contact.number}@c.us`, body: '0' };
let queue = await ShowQueueService(botInfo.botQueueId);
await UpdateTicketService({
ticketData: { queueId: queue.id },
ticketId: ticket.id
});
ticket = await ShowTicketService(ticket.id);
await sendDialogflowAwswer(ticket.whatsappId, ticket, msg, contact, false);
}

View File

@ -15,8 +15,11 @@ const dateToday = splitDateTime(new Date(format(new Date(), 'yyyy-MM-dd HH:mm:ss
import ListTicketServiceCache from "./ListTicketServiceCache" import ListTicketServiceCache from "./ListTicketServiceCache"
import { searchTicketCache, loadTicketsCache } from '../../helpers/TicketCache' import { searchTicketCache, loadTicketsCache } from '../../helpers/TicketCache'
import { getWbot } from "../../libs/wbot"; import { getWbot } from "../../libs/wbot";
import User from "../../models/User";
interface Request { interface Request {
@ -29,6 +32,7 @@ interface Request {
withUnreadMessages?: string; withUnreadMessages?: string;
queueIds: number[]; queueIds: number[];
unlimited?: string; unlimited?: string;
searchParamContent?: string;
} }
interface Response { interface Response {
@ -47,9 +51,12 @@ const ListTicketsService = async ({
showAll, showAll,
userId, userId,
withUnreadMessages, withUnreadMessages,
unlimited = 'false' unlimited = 'false',
searchParamContent = ""
}: Request): Promise<Response> => { }: Request): Promise<Response> => {
console.log('----------> searchParamContent: ', searchParamContent)
let whereCondition: Filterable["where"] = { [Op.or]: [{ userId }, { status: "pending" }], queueId: { [Op.or]: [queueIds, null] } }; let whereCondition: Filterable["where"] = { [Op.or]: [{ userId }, { status: "pending" }], queueId: { [Op.or]: [queueIds, null] } };
console.log('PAGE NUMBER TICKET: ', pageNumber) console.log('PAGE NUMBER TICKET: ', pageNumber)
@ -64,9 +71,6 @@ const ListTicketsService = async ({
} }
if (searchParam && searchParam.trim().length > 0 && process.env.CACHE) { if (searchParam && searchParam.trim().length > 0 && process.env.CACHE) {
try { try {
@ -147,20 +151,33 @@ const ListTicketsService = async ({
if (searchParam) { if (searchParam) {
const sanitizedSearchParam = searchParam.toLocaleLowerCase().trim(); const sanitizedSearchParam = searchParam.toLocaleLowerCase().trim();
const sanitizedSearchParamContent = searchParamContent.toLocaleLowerCase().trim();
if (searchParamContent.length > 0) {
includeCondition = [
...includeCondition,
{
model: Message,
as: "messages",
attributes: ["id", "body"],
where: {
body: where(fn("LOWER", col("body")), "LIKE", `%${sanitizedSearchParamContent}%`)
},
required: false,
duplicating: false
}
];
whereCondition = {
...whereCondition,
"$message.body$": where(fn("LOWER", col("body")), "LIKE", `%${sanitizedSearchParamContent}%`)
};
}
// includeCondition = [
// ...includeCondition,
// {
// model: Message,
// as: "messages",
// attributes: ["id", "body"],
// where: {
// body: where(fn("LOWER", col("body")), "LIKE", `%${sanitizedSearchParam}%`)
// },
// required: false,
// duplicating: false
// }
// ];
whereCondition = { whereCondition = {
...whereCondition, ...whereCondition,
@ -171,11 +188,17 @@ const ListTicketsService = async ({
{ "$contact.number$": { [Op.like]: `%${sanitizedSearchParam}%` } }, { "$contact.number$": { [Op.like]: `%${sanitizedSearchParam}%` } },
// { ],
// "$message.body$": where(fn("LOWER", col("body")), "LIKE", `%${sanitizedSearchParam}%`)
// }
]
}; };
const userProfile: any = await User.findByPk(userId)
if (userProfile.dataValues.profile != 'admin' && userProfile.dataValues.profile != 'master') {
whereCondition = { ...whereCondition, userId }
}
} }
if (date) { if (date) {
@ -201,8 +224,6 @@ const ListTicketsService = async ({
const offset = limit * (+pageNumber - 1); const offset = limit * (+pageNumber - 1);
const { count, rows: tickets } = await Ticket.findAndCountAll({ const { count, rows: tickets } = await Ticket.findAndCountAll({
where: whereCondition, where: whereCondition,
include: includeCondition, include: includeCondition,

View File

@ -9,53 +9,82 @@ import { userInfo } from "os";
import { Op, where } from "sequelize"; import { Op, where } from "sequelize";
import { Sequelize } from "sequelize"; import { Sequelize } from "sequelize";
import moment from 'moment'; import moment from 'moment';
import { startOfDay, endOfDay, parseISO, getDate} from "date-fns"; import { startOfDay, endOfDay, parseISO, getDate, subSeconds } from "date-fns";
import { string } from "yup/lib/locale"; import { string } from "yup/lib/locale";
function paramsQuery(params: string[]) {
let like = []
for (let i = 0; i < params.length; i++) {
like.push({ [Op.like]: `%${params[i]}%` })
}
return like
}
//Report by user, startDate, endDate //Report by user, startDate, endDate
const ShowTicketMessage = async (ticketId: string | number, onlyNumber: boolean = false, fromMe?:boolean, limit?: number, regexp?: string): Promise<Message[]> => { const ShowTicketMessage = async (
ticketId: string | number,
limit: number,
fromMe: boolean,
params?: string[],
onlyNumber?: boolean,
regexp?: string): Promise<Message[]> => {
let where_clause = {} let where_clause = {}
if(onlyNumber){ if (onlyNumber) {
where_clause = { where_clause = {
ticketId: ticketId, ticketId: ticketId,
fromMe: fromMe ? fromMe : 0, fromMe: fromMe ? fromMe : 0,
//body: {[Op.regexp]: '^[0-9]*$'}, body: { [Op.regexp]: regexp },
// body: {[Op.regexp]: '^[0-3]$'}, }
body: {[Op.regexp]: regexp},
}
} }
else{ else if (params && params.length > 0) {
where_clause = { where_clause = {
ticketId: ticketId, ticketId: ticketId,
fromMe: fromMe ? fromMe : 0, fromMe: fromMe ? fromMe : 0,
} updatedAt: {
[Op.between]: [+subSeconds(new Date(), 50), +new Date()],
},
body: {
[Op.or]: paramsQuery(params)
}
}
}
else {
where_clause = {
ticketId: ticketId,
fromMe: fromMe ? fromMe : 0,
}
} }
const ticket = await Message.findAll({
where: where_clause , const ticket = await Message.findAll({
limit: limit ? limit : 10000,
raw:true, where: where_clause,
attributes: ['body', 'read', 'mediaType','fromMe', 'mediaUrl', [Sequelize.fn("DATE_FORMAT",Sequelize.col("createdAt"),"%d/%m/%Y %H:%i:%s"),"createdAt"]], limit: limit,
raw: true,
attributes: ['body', 'read', 'mediaType', 'fromMe', 'mediaUrl', [Sequelize.fn("DATE_FORMAT", Sequelize.col("createdAt"), "%d/%m/%Y %H:%i:%s"), "createdAt"]],
order: [ order: [
['createdAt', 'DESC'] ['createdAt', 'DESC']
] ]
}); });
if (!ticket) { if (!ticket) {
throw new AppError("ERR_NO_TICKET_FOUND", 404); throw new AppError("ERR_NO_TICKET_FOUND", 404);
} }
return ticket; return ticket;
}; };
export default ShowTicketMessage; export default ShowTicketMessage;

View File

@ -27,7 +27,7 @@ const ShowTicketService = async (id: string | number): Promise<Ticket> => {
] ]
}); });
console.log('>>>>>>>>>>>>>>>>>>>>>>>> ShowTicketService: ',ticket?.whatsappId) // console.log('>>>>>>>>>>>>>>>>>>>>>>>> ShowTicketService: ',ticket?.whatsappId)
if (!ticket) { if (!ticket) {
throw new AppError("ERR_NO_TICKET_FOUND", 404); throw new AppError("ERR_NO_TICKET_FOUND", 404);

View File

@ -8,20 +8,23 @@ import ShowTicketService from "./ShowTicketService";
import { createOrUpdateTicketCache } from '../../helpers/TicketCache' import { createOrUpdateTicketCache } from '../../helpers/TicketCache'
import AppError from "../../errors/AppError"; import AppError from "../../errors/AppError";
import sendWhatsAppMessageSocket from "../../helpers/SendWhatsappMessageSocket";
var flatten = require('flat') var flatten = require('flat')
interface TicketData { interface TicketData {
status?: string; status?: string;
userId?: number; userId?: number;
queueId?: number; queueId?: number;
statusChatEnd?: string statusChatEnd?: string;
unreadMessages?: number;
} }
interface Request { interface Request {
ticketData: TicketData; ticketData: TicketData;
ticketId: string | number; ticketId: string | number;
msg?: string
} }
interface Response { interface Response {
@ -32,83 +35,94 @@ interface Response {
const UpdateTicketService = async ({ const UpdateTicketService = async ({
ticketData, ticketData,
ticketId ticketId,
msg=''
}: Request): Promise<Response> => { }: Request): Promise<Response> => {
try { try {
const { status, userId, queueId, statusChatEnd } = ticketData; const { status, userId, queueId, statusChatEnd, unreadMessages } = ticketData;
const ticket = await ShowTicketService(ticketId); const ticket = await ShowTicketService(ticketId);
// await SetTicketMessagesAsRead(ticket); // await SetTicketMessagesAsRead(ticket);
const oldStatus = ticket.status; const oldStatus = ticket.status;
const oldUserId = ticket.user?.id; const oldUserId = ticket.user?.id;
if (oldStatus === "closed") { if (oldStatus === "closed") {
await CheckContactOpenTickets(ticket.contact.id); await CheckContactOpenTickets(ticket.contact.id);
} }
await ticket.update({
status,
queueId,
userId,
statusChatEnd
});
await ticket.reload(); await ticket.update({
status,
// TEST DEL queueId,
try { userId,
unreadMessages,
// const { name, number } = await ShowContactService(ticket.contactId) statusChatEnd
let jsonString = JSON.stringify(ticket); //convert to string to remove the sequelize specific meta data
let ticket_obj = JSON.parse(jsonString); //to make plain json
delete ticket_obj['contact']['extraInfo']
delete ticket_obj['user']
ticket_obj = flatten(ticket_obj)
await createOrUpdateTicketCache(`ticket:${ticket.id}`, ticket_obj)
} catch (error) {
console.log('There was an error on UpdateTicketService.ts on createTicketCache: ', error)
}
//
let io = getIO();
if (ticket.status !== oldStatus || ticket.user?.id !== oldUserId) {
io.to(oldStatus).emit("ticket", {
action: "delete",
ticketId: ticket.id
}); });
}
await ticket.reload();
if (msg.length > 0) {
setTimeout(async () => {
sendWhatsAppMessageSocket(ticket, msg)
}, 2000)
}
// TEST DEL
try {
// const { name, number } = await ShowContactService(ticket.contactId)
let jsonString = JSON.stringify(ticket); //convert to string to remove the sequelize specific meta data
let ticket_obj = JSON.parse(jsonString); //to make plain json
delete ticket_obj['contact']['extraInfo']
delete ticket_obj['user']
ticket_obj = flatten(ticket_obj)
await createOrUpdateTicketCache(`ticket:${ticket.id}`, ticket_obj)
} catch (error) {
console.log('There was an error on UpdateTicketService.ts on createTicketCache: ', error)
}
//
let io = getIO();
if (ticket.status !== oldStatus || ticket.user?.id !== oldUserId) {
io.to(oldStatus).emit("ticket", {
action: "delete",
ticketId: ticket.id
});
}
io.to(ticket.status) io.to(ticket.status)
.to("notification") .to("notification")
.to(ticketId.toString()) .to(ticketId.toString())
.emit("ticket", { .emit("ticket", {
action: "update",
ticket
});
io.emit("ticketStatus", {
action: "update", action: "update",
ticket ticketStatus: { ticketId: ticket.id, status: ticket.status }
}); });
io.emit("ticketStatus", { return { ticket, oldStatus, oldUserId };
action: "update",
ticketStatus: { ticketId: ticket.id, status: ticket.status }
});
return { ticket, oldStatus, oldUserId };
} catch (error: any) { } catch (error: any) {
console.error('===> Error on UpdateTicketService.ts file: \n', error) console.error('===> Error on UpdateTicketService.ts file: \n', error)
throw new AppError(error.message); throw new AppError(error.message);
} }
}; };
export default UpdateTicketService; export default UpdateTicketService;

View File

@ -45,10 +45,11 @@ const SendWhatsAppMessage = async ({
try { try {
let timestamp = Math.floor(Date.now() / 1000) // let timestamp = Math.floor(Date.now() / 1000)
let timestamp = Date.now() + String(Math.floor(Math.random() * 1000))
var timetaken = `########################################${timestamp}| TicketId: ${ticket.id} => Time taken to send the message`; var timetaken = `########################################${timestamp}| TicketId: ${ticket.id} => Time taken to send the message`;
console.time(timetaken) console.time(timetaken)
let quotedMsgSerializedId: string | undefined; let quotedMsgSerializedId: string | undefined;
@ -67,9 +68,15 @@ const SendWhatsAppMessage = async ({
let listWhatsapp = null let listWhatsapp = null
// listWhatsapp = await searchWhatsappCache(`${ticket.whatsappId}`, 'CONNECTED') // listWhatsapp = await searchWhatsappCache(`${ticket.whatsappId}`, 'CONNECTED')
console.log('ticket.whatsappIdticket.whatsappIdticket.whatsappIdticket: ', ticket.whatsappId) if (!ticket.whatsappId) {
const defaultWhatsapp: any = await GetDefaultWhatsApp(ticket.userId);
await ticket.update({ whatsappId: +defaultWhatsapp.id });
}
if (!listWhatsapp) { if (!listWhatsapp) {
listWhatsapp = await ListWhatsAppsNumber(ticket.whatsappId, 'CONNECTED') listWhatsapp = await ListWhatsAppsNumber(ticket.whatsappId, 'CONNECTED')
@ -77,12 +84,8 @@ const SendWhatsAppMessage = async ({
if (listWhatsapp.whatsapp && listWhatsapp.whatsapp.status != 'CONNECTED' && listWhatsapp.whatsapps.length > 0) { if (listWhatsapp.whatsapp && listWhatsapp.whatsapp.status != 'CONNECTED' && listWhatsapp.whatsapps.length > 0) {
// console.log('kkkkkkkkkkkkkkkkkkkkkkkkkkkk: ', listWhatsapp.whatsapps[0].id)
await ticket.update({ whatsappId: + listWhatsapp.whatsapps[0].id }); await ticket.update({ whatsappId: + listWhatsapp.whatsapps[0].id });
let _ticket = await Ticket.findByPk(listWhatsapp.whatsapps[0].id)
} }
@ -123,9 +126,9 @@ const SendWhatsAppMessage = async ({
try { try {
sendWhatsAppMessageSocket(ticket, body, quotedMsgSerializedId, number); sendWhatsAppMessageSocket(ticket, body, quotedMsgSerializedId, number);
await ticket.update({ lastMessage: body }); await ticket.update({ lastMessage: body });
await updateTicketCacheByTicketId(ticket.id, { lastMessage: body, updatedAt: new Date(ticket.updatedAt).toISOString() }) await updateTicketCacheByTicketId(ticket.id, { lastMessage: body, updatedAt: new Date(ticket.updatedAt).toISOString() })

View File

@ -71,6 +71,10 @@ import sendWhatsAppMessageSocket from "../../helpers/SendWhatsappMessageSocket";
import { getWhatsappIds, setWhatsappId } from "../../helpers/WhatsappIdMultiSessionControl"; import { getWhatsappIds, setWhatsappId } from "../../helpers/WhatsappIdMultiSessionControl";
import SendWhatsAppMedia from "./SendWhatsAppMedia"; import SendWhatsAppMedia from "./SendWhatsAppMedia";
import AppError from "../../errors/AppError"; import AppError from "../../errors/AppError";
import { tr } from "date-fns/locale";
import mostRepeatedPhrase from "../../helpers/MostRepeatedPhrase";
import FindOrCreateTicketServiceBot from "../TicketServices/FindOrCreateTicketServiceBot";
import { setMessageAsRead } from "../../helpers/SetMessageAsRead";
@ -223,23 +227,44 @@ const queryEndPointHit = async (centro_de_custo: string) => {
let msg_endpoint: any = [] let msg_endpoint: any = []
let response2 = await endPointQuery('http://177.107.193.124:8095/labs/zabbix-frontend/api/api.php', 'post', centro_de_custo.trim()) let response2 = await endPointQuery('http://177.107.193.124:8095/labs/zabbix-frontend/api/api.php', 'post', { 'params[cod_web]': centro_de_custo.trim(), })
if (response2 && response2.data.result) { if (response2 && response2.data.result) {
response2 = response2.data.result; response2 = response2.data.result;
for (let i = 0; i < response2.length; i++) { for (let i = 0; i < response2.length; i++) {
let data = '' let data = ''
let sub_data = '*Atualizações:*\n\n' let sub_data = '*Atualizações:*\n\n'
const properties: any = Object.entries(response2[i]); let properties: any = Object.entries(response2[i]);
for (let x = 0; x < properties.length; x++) { for (let x = 0; x < properties.length; x++) {
if (typeof (properties[x][1]) != 'object') { if (typeof (properties[x][1]) != 'object') {
if (properties[x][0] === 'n_chamado_web') {
properties[x][0] = 'Protocolo'
}
else if (properties[x][0] === 'nome_cliente') {
properties[x][0] = 'Nome do cliente'
}
else if (properties[x][0] === 'quando_inicio') {
properties[x][0] = 'Data de abertura'
}
else if (properties[x][0] === 'nome_filial') {
properties[x][0] = 'Nome da filial'
}
else if (properties[x][0] === 'cod_web') {
properties[x][0] = 'Codigo do cliente'
}
else if (properties[x][0] === 'id' || properties[x][0] === 'cliente_id') {
continue
}
data += `*${properties[x][0]}*: ${properties[x][1].replace(/(\r\n|\n|\r)/gm, "")}\n` data += `*${properties[x][0]}*: ${properties[x][1].replace(/(\r\n|\n|\r)/gm, "")}\n`
} }
@ -249,10 +274,20 @@ const queryEndPointHit = async (centro_de_custo: string) => {
for (let k = 0; k < sub_properties.length; k++) { for (let k = 0; k < sub_properties.length; k++) {
const inner_properties: any = Object.entries(sub_properties[k]); let inner_properties: any = Object.entries(sub_properties[k]);
for (let y = 0; y < inner_properties.length; y++) { for (let y = 0; y < inner_properties.length; y++) {
if (inner_properties[y][0] === 'texto') {
inner_properties[y][0] = 'Informação'
}
else if (inner_properties[y][0] === 'quando') {
inner_properties[y][0] = 'Data da Informação'
}
else if (inner_properties[y][0] === 'login') {
continue
}
sub_data += `*${inner_properties[y][0]}*: ${inner_properties[y][1].replace(/(\r\n|\n|\r)/gm, "")}\n` sub_data += `*${inner_properties[y][0]}*: ${inner_properties[y][1].replace(/(\r\n|\n|\r)/gm, "")}\n`
} }
@ -324,7 +359,7 @@ const monitoramento_response2 = async (response: any | null, wbot: any, contact:
async function sendDelayedMessages(wbot: Session, ticket: Ticket, contact: Contact, message: string, _msg?: WbotMessage) { async function sendDelayedMessages(wbot: any, ticket: Ticket, contact: Contact, message: string, _msg?: WbotMessage) {
const body = message.replace(/\\n/g, '\n'); const body = message.replace(/\\n/g, '\n');
if (body.search('dialog_actions') != -1) { if (body.search('dialog_actions') != -1) {
@ -335,204 +370,132 @@ async function sendDelayedMessages(wbot: Session, ticket: Ticket, contact: Conta
if (msgAction.actions[0] == 'request_endpoint') { if (msgAction.actions[0] == 'request_endpoint') {
// OLD
// const sentMessage = await wbot.sendMessage(`${contact.number}@c.us`, msgAction.msgBody);
// await verifyMessage(sentMessage, ticket, contact);
// await new Promise(f => setTimeout(f, 1000));
// NEW // NEW
await SendWhatsAppMessage({ body: msgAction.msgBody, ticket, number: `${contact.number}@c.us` }) await SendWhatsAppMessage({ body: msgAction.msgBody, ticket, number: `${contact.number}@c.us` })
// const url = 'http://177.107.193.124:8095/labs/zabbix-frontend/api/api.php/99383'
// const url = 'https://sos.espacolaser.com.br/api/whatsapps/ticket/R32656' let aux = msgAction.actions[1].split('/')
let endPointResponse = await endPointQuery(msgAction.actions[1], 'get')
console.log('Object.entries(endPointResponse.data).length: ', Object.entries(endPointResponse.data).length) let cod_web
// console.log('endPointResonse.status: ',typeof(endPointResponse.status))
if (endPointResponse && endPointResponse.status == 200 && Object.entries(endPointResponse.data).length > 0) { if (aux && aux.length > 0) {
cod_web = aux[aux.length - 1]
}
// endPointResponse.data.categoria = 'ELOS' let params = msgAction.actions[1].split('api.php')[1].slice(1).split('/')
// endPointResponse.data.categoria = 'INFRAESTRUTURA'
// endPointResponse.data.subcategoria = 'INTERNET'
// endPointResponse.data.terceiro_nivel = 'QUEDA TOTAL'
console.log('-------------> endPointResponse.data.categoria: ', endPointResponse.data.categoria) let url = msgAction.actions[1].replace(/\.php.*/, '.php')
if (endPointResponse.data.categoria != 'INFRAESTRUTURA' && endPointResponse.data.categoria != 'ELOS') { let response: any = ''
botSendMessage(ticket, contact, wbot, `A categorização desse chamado não se enquadra no atendimento deste canal!\n\n _Digite *0* para voltar ao menu principal._`)
return
}
const response = Object.entries(endPointResponse.data); if (params[0] === 'validate_n_chamado_web') {
let msg_endpoint_response = ''
let msg_endpoint2: any = [] let valid = await endPointQuery(
let centro_de_custo = '' 'http://177.107.192.247:8095/labs/monitoramentohit/api/api.php',
'post',
{
'params[n_chamado_web]': params[1],
'method': 'omnihit.consultachamadostatus'
})
for (let i = 0; i < response.length; i++) { if (valid && valid.data.result == 'open') {
if (['id_solicitante', 'centro_custo_departamento', 'departamento_do_formulario', 'centro_de_custo_do_departamento_do_formulario'].includes(response[i][0])) botSendMessage(ticket, `Protocolo validado, por favor, pode digitar o texto a ser adicionado no histórico do protocolo *${params[1]}*`)
continue
msg_endpoint_response += `*${response[i][0]}*: ${String(response[i][1]).replace(/(<([^>]+)>)/gi, "")}\n`
} }
else if (valid && valid.data.result == 'notfound') {
botSendMessage(ticket, `Protocolo *${params[1]}* não encontrado!\n_Digite *0* para falar com a HIT._`)
if (endPointResponse.data.centro_custo_departamento && endPointResponse.data.centro_custo_departamento.trim().length > 0 && (endPointResponse.data.categoria == 'INFRAESTRUTURA' && endPointResponse.data.subcategoria == 'INTERNET')) {
centro_de_custo = endPointResponse.data.centro_custo_departamento
msg_endpoint2 = await queryEndPointHit(centro_de_custo)
}
const itsm_response = async (message1: string = '', message2: string = '') => {
// OLD
// const msg = await wbot.sendMessage(`${contact.number}@c.us`, `*Situação do chamado ${extractCallCode(msgAction.msgBody)}:*${message1}${msg_endpoint_response}${message2}\n_Digite *0* para voltar ao menu principal._`);
// await verifyMessage(msg, ticket, contact);
// await new Promise(f => setTimeout(f, 1000));
// NEW
await SendWhatsAppMessage({ body: `*Situação do chamado ${extractCallCode(msgAction.msgBody)}:*${message1}${msg_endpoint_response}${message2}\n_Digite *0* para voltar ao menu principal._`, ticket, number: `${contact.number}@c.us` })
} }
else if (valid && valid.data.result == 'close') {
botSendMessage(
ticket,
`O protocolo *${params[1]}* foi encerrado. Não é mais possível adicionar informação. Se desejar consultar o historico digite *1*`)
const sendMessageBot = async (message1: string = '', message2: string = '') => { //
// OLD
// const msg = await wbot.sendMessage(`${contact.number}@c.us`, `${message1}${message2}`);
// await verifyMessage(msg, ticket, contact);
// await new Promise(f => setTimeout(f, 1000));
// NEW
await SendWhatsAppMessage({ body: `${message1}${message2}`, ticket, number: `${contact.number}@c.us` })
}
if (body.search('dialog_options') != -1) {
let msgAction = botMsgActions(body)
let index = msgAction.actions.findIndex((i) => i == 'dialog_options')
if (index != -1) {
let dialog_options = msgAction.actions[index + 1]
if (dialog_options == '3' && endPointResponse.data.categoria == 'ELOS') {
index = msgAction.actions.findIndex((i) => i == 'queue_transfer')
if (index != -1) {
await itsm_response('\n\n')
await monitoramento_response2(msg_endpoint2, wbot, contact, ticket, centro_de_custo, '\n\n', false)
botSendMessage(ticket, contact, wbot, `Estamos direcionando seu atendimento para o Suporte. Em breve você será atendido por um de nossos atendentes!\n\nPara voltar ao atendimento *automatizado* e sair da fila de atendimento *humano* digite *0*`)
await transferTicket(+msgAction.actions[index + 1], wbot, ticket, contact)
}
}
}
}
else if (
(endPointResponse.data.categoria == 'INFRAESTRUTURA' && endPointResponse.data.subcategoria == 'INTERNET' &&
endPointResponse.data.terceiro_nivel == 'QUEDA TOTAL') ||
(endPointResponse.data.categoria == 'INFRAESTRUTURA' && endPointResponse.data.subcategoria == 'INTERNET' &&
endPointResponse.data.terceiro_nivel == 'PROBLEMA DE LENTIDÃO') ||
(endPointResponse.data.categoria == 'ELOS' && endPointResponse.data.subcategoria == 'VENDAS') ||
(endPointResponse.data.categoria == 'ELOS' && endPointResponse.data.subcategoria == 'INDISPONIBILIDADE')
) {
await itsm_response('\n\n')
if (endPointResponse.data.categoria == 'INFRAESTRUTURA' && endPointResponse.data.subcategoria == 'INTERNET' && centro_de_custo) {
await monitoramento_response2(msg_endpoint2, wbot, contact, ticket, centro_de_custo, '\n\n _Digite *0* para voltar ao menu principal._', true)
}
await sendMessageBot('Se deseja solicitar atendimento de urgência, digite *1*!', '\n\n _Digite *0* para voltar ao menu principal._')
return
} }
else { else {
//await sendMessageBot('A categorização desse chamado não se enquadra no atendimento deste canal!', '\n\n _Digite *0* para voltar ao menu principal._')
await itsm_response('\n\n')
if (endPointResponse.data.categoria == 'INFRAESTRUTURA' && endPointResponse.data.subcategoria == 'INTERNET' && centro_de_custo) { botSendMessage(ticket, `Ops! Não foi possível validar seu protocolo devido a um erro na comunicação com o servidor.Tente novamente mais tarde.\n_Digite *0* para falar com a HIT._`)
await monitoramento_response2(msg_endpoint2, wbot, contact, ticket, centro_de_custo, '\n\n _Digite *0* para voltar ao menu principal._', true)
}
await sendMessageBot('Acompanhe a evolução do atendimento através do SOS')
return
} }
// else if ((endPointResponse.data.categoria == 'INFRAESTRUTURA' || endPointResponse.data.subcategoria == 'INTERNET' || }
// endPointResponse.data.terceiro_nivel == 'QUEDA TOTAL' || endPointResponse.data.terceiro_nivel == 'PROBLEMA DE LENTIDÃO') ||
// (endPointResponse.data.terceiro_nivel == 'PROBLEMA DE LENTIDÃO' || endPointResponse.data.terceiro_nivel == 'ABERTO')) {
// await itsm_response('', `\n Estamos direcionando seu atendimento para o Suporte. Em breve você será atendido por um de nossos atendentes!`) if (params[0] === 'validate_n_chamado_web') return
// await monitoramento_response(msg_endpoint2) if (params[0] === 'cod_web') {
// await transferTicket(0, wbot, ticket, contact) response = await endPointQuery('http://177.107.192.247:8095/labs/monitoramentohit/api/api.php',
'post', {
'params[cod_web]': params[1],
'method': 'omnihit.consultachamado'
})
// }
// else {
// await itsm_response('', `\n Por favor, aguarde atendimento e acompanhe sua solicitação no SOS.`)
// await monitoramento_response(msg_endpoint2)
// }
} }
else if (endPointResponse && endPointResponse.status == 200 && Object.entries(endPointResponse.data).length == 0) { else if (params[0] === 'n_chamado_web') {
botSendMessage(ticket, contact, wbot, `Não existe nenhum chamado para consulta com esse número!\n _Digite *0* para voltar ao menu principal._`)
return response = await endPointQuery('http://177.107.192.247:8095/labs/monitoramentohit/api/api.php',
'post',
{
'params[n_chamado_web]': params[1],
'method': 'omnihit.consultachamado',
})
} }
else if (endPointResponse && endPointResponse.status == 500) {
botSendMessage(ticket, contact, wbot, `Houve um erro ao realizar a consulta no sos espacolaser!\n _Digite *0* para voltar ao menu principal._`) try {
return
} if (response && response.data.result.trim().length > 0) {
else { response = response.data.result
botSendMessage(ticket, contact, wbot, `Desculpe, nao foi possível realizar a consulta!\n _Digite *0* para voltar ao menu principal._`) }
return else if (response && response.data.result.trim().length === 0) {
response = ''
}
else {
response = null
}
// response = response.data.result
if (response.trim().length == 0) {
botSendMessage(ticket, `Não existe nenhum chamado para essa operação!\n _Digite *0* para falar com a HIT._`)
}
else if (response.trim().length > 0) {
await SendWhatsAppMessage({ body: response, ticket });
}
} catch (error) {
botSendMessage(ticket, `Houve um erro ao realizar a consulta!\n _Digite *0* para falar com a HIT._`)
} }
} else if (msgAction.actions[0] == 'queue_transfer') { } else if (msgAction.actions[0] == 'queue_transfer') {
console.log('>>>>>>>>>>>>>>> msgAction: ', msgAction, ' | msgAction.actions[1]: ', msgAction.actions[1]) console.log('>>>>>>>>>>>>>>> msgAction: ', msgAction, ' | msgAction.actions[1]: ', msgAction.actions[1])
// const contact_custom_field = await ShowContactCustomFieldService(contact.id) const contact_custom_field = await ShowContactCustomFieldService(contact.id)
// if (contact_custom_field && contact_custom_field.length > 0) { if (contact_custom_field && contact_custom_field.length > 0) {
// const msg_endpoint = await queryEndPointHit(contact_custom_field[0].value) const msg_endpoint = await queryEndPointHit(contact_custom_field[0].value)
// await monitoramento_response2(msg_endpoint, wbot, contact, ticket, contact_custom_field[0].value) await monitoramento_response2(msg_endpoint, wbot, contact, ticket, contact_custom_field[0].value)
// } }
// console.log('************* contact_custom_field: ', contact_custom_field) console.log('************* contact_custom_field: ', contact_custom_field)
// OLD // OLD
// const msg = await wbot.sendMessage(`${contact.number}@c.us`, msgAction.msgBody); // const msg = await wbot.sendMessage(`${contact.number}@c.us`, msgAction.msgBody);
@ -592,20 +555,22 @@ const extractCallCode = (str: string) => {
return '' return ''
} }
const sendDialogflowAwswer = async ( const sendDialogflowAnswer = async (
wbot: Session, wbot: any,
ticket: Ticket, ticket: Ticket,
msg: any, msg: any,
contact: Contact, contact: Contact,
chat: Chat, chat: Chat,
startDialog: boolean = false startDialog: boolean = false,
send: boolean = true
// chat: Chat
) => { ) => {
if (startDialog){ if (startDialog) {
msg.body = 'menu' msg.body = 'menu'
console.log('<--------------------- VOLTOU AO MENU ---------------->') console.log('<--------------------- VOLTOU AO MENU ---------------->')
} }
console.log('uuuuuuuuuuuuuuuuuuuuuuuuuuu 04') console.log('uuuuuuuuuuuuuuuuuuuuuuuuuuu 04')
@ -620,14 +585,14 @@ const sendDialogflowAwswer = async (
console.log('uuuuuuuuuuuuuuuuuuuuuuuuuuu 4') console.log('uuuuuuuuuuuuuuuuuuuuuuuuuuu 4')
if (msg.type != 'chat') { if (msg.type != 'chat') {
botSendMessage(ticket, contact, wbot, `Desculpe, nao compreendi!\nEnvie apenas texto quando estiver interagindo com o bot!\n _Digite *0* para voltar ao menu principal._`) botSendMessage(ticket, `Desculpe, nao compreendi!\nEnvie apenas texto quando estiver interagindo com o bot!\n _Digite *0* para falar com a HIT._`)
return return
} }
console.log('uuuuuuuuuuuuuuuuuuuuuuuuuuu 5') console.log('uuuuuuuuuuuuuuuuuuuuuuuuuuu 5')
if (msg.type == 'chat' && String(msg.body).length > 120) { if (msg.type == 'chat' && String(msg.body).length > 120) {
botSendMessage(ticket, contact, wbot, `Desculpe, nao compreendi!\nTexto acima de 120 caracteres!\n _Digite *0* para voltar ao menu principal._`) botSendMessage(ticket, `Desculpe, nao compreendi!\nTexto acima de 120 caracteres!\n _Digite *0* para falar com a HIT._`)
return return
} }
@ -644,8 +609,10 @@ const sendDialogflowAwswer = async (
return; return;
} }
if (!send) return
// Make disponible later from session out // Make disponible later from session out
// chat.sendStateTyping(); // chat.sendStateTyping();
await new Promise(f => setTimeout(f, 1000)); await new Promise(f => setTimeout(f, 1000));
@ -772,12 +739,11 @@ const verifyQueue = async (
console.log('uuuuuuuuuuuuuuuuuuuuuuuuuuu 3') console.log('uuuuuuuuuuuuuuuuuuuuuuuuuuu 3')
const chat = wbot.chat const chat = wbot.chat
await sendDialogflowAwswer(wbot, _ticket, msg, contact, chat, aceppt); await sendDialogflowAnswer(wbot, _ticket, msg, contact, chat, aceppt);
console.log('uuuuuuuuuuuuuuuuuuuuuuuuuuu 03') console.log('uuuuuuuuuuuuuuuuuuuuuuuuuuu 03')
return return
} }
// //
@ -792,9 +758,6 @@ const verifyQueue = async (
body = `\u200e${choosenQueue.greetingMessage}`; body = `\u200e${choosenQueue.greetingMessage}`;
} }
// const sentMessage = await wbot.sendMessage(`${contact.number}@c.us`, body);
// await verifyMessage(sentMessage, ticket, contact);
sendWhatsAppMessageSocket(ticket, body) sendWhatsAppMessageSocket(ticket, body)
// console.log('uuuuuuuuuuuuuuuuuuuuuuuuuuu 3 body: ',body) // console.log('uuuuuuuuuuuuuuuuuuuuuuuuuuu 3 body: ',body)
@ -802,10 +765,9 @@ const verifyQueue = async (
} }
else { else {
const repet: any = await mostRepeatedPhrase(ticket.id)
//test del transfere o atendimento se entrar na ura infinita if (repet.occurrences > 4) {
let ticket_message = await ShowTicketMessage(ticket.id, false);
if (ticket_message.length > 20) {
await UpdateTicketService({ ticketData: { status: 'pending', queueId: queues[0].id }, ticketId: ticket.id }); await UpdateTicketService({ ticketData: { status: 'pending', queueId: queues[0].id }, ticketId: ticket.id });
@ -835,8 +797,8 @@ const verifyQueue = async (
bodySplited.forEach(e => { bodySplited.forEach(e => {
if(e && e.trim().length>0){ if (e && e.trim().length > 0) {
console.log('ELEMENT e: ',e) console.log('ELEMENT e: ', e)
sendWhatsAppMessageSocket(ticket, e) sendWhatsAppMessageSocket(ticket, e)
} }
@ -858,7 +820,7 @@ const verifyQueue = async (
} }
}; };
const transferTicket = async (queueIndex: number, wbot: Session, ticket: Ticket, contact: Contact) => { const transferTicket = async (queueIndex: number, wbot: any, ticket: Ticket, contact: Contact) => {
const botInfo = await BotIsOnQueue('botqueue', contact.accept) const botInfo = await BotIsOnQueue('botqueue', contact.accept)
@ -868,10 +830,13 @@ const transferTicket = async (queueIndex: number, wbot: Session, ticket: Ticket,
// console.log('queues ---> ', queues) // console.log('queues ---> ', queues)
await botTransferTicket(queues[queueIndex], ticket, contact, wbot) await botTransferTicket(queues[queueIndex], ticket)
} }
// const botMsgActions = (params: string) => { // const botMsgActions = (params: string) => {
// let lstActions = params.split('dialog_actions=') // let lstActions = params.split('dialog_actions=')
@ -914,7 +879,7 @@ const isValidMsg = (msg: WbotMessage): boolean => {
}; };
const queuesOutBot = async (wbot: Session, botId: string | number) => { const queuesOutBot = async (wbot: any, botId: string | number) => {
const { queues, greetingMessage } = await ShowWhatsAppService(wbot.id!); const { queues, greetingMessage } = await ShowWhatsAppService(wbot.id!);
@ -928,7 +893,9 @@ const queuesOutBot = async (wbot: Session, botId: string | number) => {
} }
const botTransferTicket = async (queues: Queue, ticket: Ticket, contact: Contact, wbot: Session) => { const botTransferTicket = async (queues: Queue, ticket: Ticket) => {
console.log('>>>>>>>>>>>>>>>>> queues.id: ', queues.id)
await ticket.update({ userId: null }); await ticket.update({ userId: null });
@ -936,7 +903,7 @@ const botTransferTicket = async (queues: Queue, ticket: Ticket, contact: Contact
} }
const botSendMedia = async (ticket: Ticket, contact: Contact, wbot: Session, mediaPath: string, fileNameExtension: string) => { const botSendMedia = async (ticket: Ticket, contact: Contact, wbot: any, mediaPath: string, fileNameExtension: string) => {
const debouncedSentMessage = debounce( const debouncedSentMessage = debounce(
@ -965,7 +932,7 @@ const botSendMedia = async (ticket: Ticket, contact: Contact, wbot: Session, med
} }
const botSendMessage = (ticket: Ticket, contact: Contact, wbot: Session, msg: string) => { const botSendMessage = (ticket: Ticket, msg: string) => {
const debouncedSentMessage = debounce( const debouncedSentMessage = debounce(
@ -1016,7 +983,7 @@ const handleMessage = async (
if (index == -1) { if (index == -1) {
// console.log('-----------------> LST: ', lst) // console.log('-----------------> LST: ', lst):q
lst.push({ id: msg.id.id }) lst.push({ id: msg.id.id })
@ -1107,6 +1074,12 @@ const handleMessage = async (
// const chat = await msg.getChat(); // const chat = await msg.getChat();
const chat = wbot.chat const chat = wbot.chat
// if(chat.isGroup){
// console.log('This message is from a Group and will be ignored!')
// return
// }
// console.log('----------> chat: ', JSON.parse(JSON.stringify(chat))) // console.log('----------> chat: ', JSON.parse(JSON.stringify(chat)))
// if (chat.isGroup) { // if (chat.isGroup) {
@ -1129,7 +1102,7 @@ const handleMessage = async (
const unreadMessages = msg.fromMe ? 0 : chat.unreadCount; const unreadMessages = msg.fromMe ? 0 : chat.unreadCount;
const contact = await verifyContact(msgContact); const contact: any = await verifyContact(msgContact);
// console.log('----------> contact: ', JSON.parse(JSON.stringify(contact))) // console.log('----------> contact: ', JSON.parse(JSON.stringify(contact)))
@ -1145,15 +1118,53 @@ const handleMessage = async (
// } // }
let ticket
if (unreadMessages === 0 && whatsapp.farewellMessage && whatsapp.farewellMessage === msg.body) return; const _botInfo = await BotIsOnQueue('botqueue')
if (_botInfo.isOnQueue) {
let ticket_obj: any = await FindOrCreateTicketServiceBot(
contact,
wbot.id!,
unreadMessages,
// groupContact
);
ticket = ticket_obj.ticket
if (ticket_obj.created) {
let queue = await ShowQueueService(_botInfo.botQueueId);
await UpdateTicketService({
ticketData: { queueId: queue.id },
ticketId: ticket.id
});
ticket = await ShowTicketService(ticket.id);
}
}
else {
ticket = await FindOrCreateTicketService(
contact,
wbot.id!,
unreadMessages,
// groupContact
);
}
// const ticket = await FindOrCreateTicketService(
// contact,
// wbot.id!,
// unreadMessages,
// // groupContact
// );
const ticket = await FindOrCreateTicketService(
contact,
wbot.id!,
unreadMessages,
// groupContact
);
// //
// await updateTicketCacheByTicketId(ticket.id, {'contact.profilePicUrl': ticket.contact.profilePicUrl}) // await updateTicketCacheByTicketId(ticket.id, {'contact.profilePicUrl': ticket.contact.profilePicUrl})
@ -1190,9 +1201,7 @@ const handleMessage = async (
!ticket.userId && !ticket.userId &&
whatsapp.queues.length >= 1 whatsapp.queues.length >= 1
) { ) {
await verifyQueue(wbot, msg, ticket, contact); await verifyQueue(wbot, msg, ticket, contact);
} }
// O bot interage com o cliente e encaminha o atendimento para fila de atendende quando o usuário escolhe a opção falar com atendente // O bot interage com o cliente e encaminha o atendimento para fila de atendende quando o usuário escolhe a opção falar com atendente
@ -1202,40 +1211,75 @@ const handleMessage = async (
// const botInfo = { isOnQueue: false, botQueueId: 0, userIdBot: 0 } // const botInfo = { isOnQueue: false, botQueueId: 0, userIdBot: 0 }
if (botInfo.isOnQueue && !msg.fromMe && ticket.userId == botInfo.userIdBot) { if (botInfo.isOnQueue && !msg.fromMe && ticket.userId == botInfo.userIdBot) {
// TEST DEL const repet: any = await mostRepeatedPhrase(ticket.id)
let test: any = await ShowTicketMessage(ticket.id, false, true, 5);
if (test && test.length > 0 && test[0].body.includes('Se deseja solicitar atendimento de urgência, digite *1*') && msg.body == '1') { console.log('repet.occurrences: ', repet.occurrences)
console.log('===================================> ENDPOINT REQUEST') if (repet.occurrences > 4) {
for (let i = 0; i < test.length; i++) { await transferTicket(0, wbot, ticket, contact.accept)
if (test[i].body.includes('*categoria*: INFRAESTRUTURA')) { await SendWhatsAppMessage({
body: `Seu atendimento foi transferido para um agente!\n\nPara voltar ao menu principal digite *0*
`, ticket, number: `${contact.number}@c.us`
})
botSendMessage(ticket, contact, wbot, `Estamos direcionando seu atendimento para o Suporte. Em breve você será atendido por um de nossos atendentes!\n\nPara voltar ao atendimento *automatizado* e sair da fila de atendimento *humano* digite *0*`) }
else {
await transferTicket(0, wbot, ticket, contact) let last_messages = await ShowTicketMessage(ticket.id, 2, true)
break
if (last_messages.length > 0 && last_messages[0].body.includes('validado') && msg.body.trim() != '0') {
// botSendMessage(ticket,`Aguarde, inserindo informação...\n_Digite *0* para falar com a HIT._`)
await SendWhatsAppMessage({ body: `Aguarde inserindo informação, em breve te atualizaremos`, ticket })
let aux_msg = last_messages[0].body
aux_msg = aux_msg.split('\n')[0]
let index = aux_msg.indexOf('do protocolo ')
aux_msg = aux_msg.substring(index, aux_msg.length)
let regex = /[0-9-]+/g;
let matches: any = aux_msg.match(regex);
console.log("~~~~~~~~~~~~~~~~~~~~~~> matches.join(''): ", matches.join(''));
let response = await endPointQuery(
'http://177.107.192.247:8095/labs/monitoramentohit/api/api.php',
'post',
{
'params[n_chamado_web]': matches.join(''),
'method': 'omnihit.chamadoaddobs',
'params[obs]': msg.body
})
if (response && response.data.result) {
botSendMessage(ticket, `${response.data.result}`)
} }
else if (test[i].body.includes('*categoria*: ELOS')) { else {
botSendMessage(ticket, contact, wbot, `Estamos direcionando seu atendimento para o Suporte. Em breve você será atendido por um de nossos atendentes!\n\nPara voltar ao atendimento *automatizado* e sair da fila de atendimento *humano* digite *0*`) botSendMessage(ticket, `Ops! Houve um erro ao tentar inserir sua informação devido a um erro na comunicação com o servidor.Tente novamente mais tarde.\n_Digite *0* para falar com a HIT._`)
await transferTicket(1, wbot, ticket, contact)
break
} }
}
else {
await sendDialogflowAnswer(wbot, ticket, msg, contact, chat, contact.accept);
} }
return
} }
//
await sendDialogflowAwswer(wbot, ticket, msg, contact, chat);
} }
else if (botInfo.isOnQueue && !msg.fromMe && msg.body == '0' && ticket.status == 'pending' && ticket.queueId) { else if (botInfo.isOnQueue && !msg.fromMe && msg.body == '0' && ticket.status == 'pending' && ticket.queueId) {
@ -1251,54 +1295,28 @@ const handleMessage = async (
// const chat = await msg.getChat(); // const chat = await msg.getChat();
const chat = wbot.chat const chat = wbot.chat
await sendDialogflowAwswer(wbot, _ticket, msg, contact, chat); await sendDialogflowAnswer(wbot, _ticket, msg, contact, chat, contact.aceppt);
return return
} }
// if (msg.body.trim() == 'broken') { if (msg && !msg.fromMe && ticket.status == 'pending') {
// throw new Error('Throw makes it go boom!')
// } await setMessageAsRead(ticket)
//
}
} catch (err) { } catch (err) {
Sentry.captureException(err); Sentry.captureException(err);
console.log('xxxxxxxxxxxxx err: ', err)
logger.error(`Error handling whatsapp message: Err: ${err}`); logger.error(`Error handling whatsapp message: Err: ${err}`);
const whatsapp = await ShowWhatsAppService(wbot.id!);
//Solução para contornar erro de sessão
if ((`${err}`).includes("Evaluation failed: r")) {
const sourcePath = path.join(__dirname, `../../../.wwebjs_auth/sessions/log`)
let log = new Date(new Date() + 'UTC');
const dateToday = splitDateTime(new Date(format(new Date(), 'yyyy-MM-dd HH:mm:ss', { locale: ptBR })))
if (whatsapp.status == 'CONNECTED') {
let timestamp = Math.floor(Date.now() / 1000)
fs.writeFile(`${sourcePath}/${timestamp}_wbotMessageListener.txt`, `Whatsapp id: ${whatsapp.id} \nDate: ${dateToday.fullDate} ${dateToday.fullTime} \nFile: wbotMessageListener.ts \nError: ${err}`, (error) => { });
// await restartWhatsSession(whatsapp)
}
}
else if (`${err}`.includes('[object Object]')) {
await _restore(whatsapp, 'auto_object_error')
}
} }
}; };
const handleMsgAck = async (msg_id: any, ack: any) => { const handleMsgAck = async (msg_id: any, ack: any) => {
await new Promise(r => setTimeout(r, 500));
await new Promise(r => setTimeout(r, 4000));
const io = getIO(); const io = getIO();
@ -1314,6 +1332,7 @@ const handleMsgAck = async (msg_id: any, ack: any) => {
] ]
}); });
if (!messageToUpdate) { if (!messageToUpdate) {
console.log(`Not found the MESSAGE ID ${msg_id}to change the ACK into the Message table`)
return; return;
} }
await messageToUpdate.update({ ack }); await messageToUpdate.update({ ack });
@ -1331,19 +1350,19 @@ const handleMsgAck = async (msg_id: any, ack: any) => {
} }
}; };
const wbotMessageListener = (wbot: Session): void => { const wbotMessageListener = (wbot: any): void => {
wbot.on("message_create", async msg => { wbot.on("message_create", async (msg: any) => {
handleMessage(msg, wbot); handleMessage(msg, wbot);
}); });
wbot.on("media_uploaded", async msg => { wbot.on("media_uploaded", async (msg: any) => {
handleMessage(msg, wbot); handleMessage(msg, wbot);
}); });
wbot.on("message_ack", async (msg, ack) => { wbot.on("message_ack", async (msg: any, ack: any) => {
handleMsgAck(msg, ack); handleMsgAck(msg, ack);
}); });
}; };
export { wbotMessageListener, handleMessage, handleMsgAck, lst }; export { wbotMessageListener, handleMessage, handleMsgAck, lst, verifyContact, sendDialogflowAnswer };

BIN
frontend/src.7z 100644

Binary file not shown.

View File

@ -0,0 +1,156 @@
import React, { useState, useEffect, useContext, useRef, useCallback } from "react";
import { useHistory } from "react-router-dom";
import { toast } from "react-toastify";
import Button from "@material-ui/core/Button";
import Dialog from "@material-ui/core/Dialog";
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";
import DialogContent from "@material-ui/core/DialogContent";
import DialogTitle from "@material-ui/core/DialogTitle";
import { i18n } from "../../translate/i18n";
import ButtonWithSpinner from "../ButtonWithSpinner";
import { AuthContext } from "../../context/Auth/AuthContext";
import toastError from "../../errors/toastError";
import api from "../../services/api";
const useStyles = makeStyles((theme) => ({
maxWidth: {
width: "100%",
},
paper: {
minWidth: "300px"
},
linearProgress: {
marginTop: "5px"
}
}));
const ContactCreateTicketModal = ({ modalOpen, onClose, contactId }) => {
const { user } = useContext(AuthContext);
let isMounted = useRef(true)
const history = useHistory();
const [queues, setQueues] = useState([]);
const [loading, setLoading] = useState(false);
const [selectedQueue, setSelectedQueue] = useState('');
const [itemHover, setItemHover] = useState(-1)
const classes = useStyles();
useEffect(() => {
const userQueues = user.queues.map(({ id, name, color }) => { return { id, name, color } })
if (userQueues.length === 1) setSelectedQueue(userQueues[0].id)
setQueues(userQueues)
}, [user]);
const handleClose = () => {
onClose();
};
const handleSaveTicket = useCallback(async (contactId, userId, queueId) => {
if (!contactId || !userId) {
console.log("Missing contactId or userId")
return
};
if (!queueId) {
toast.warning("Nenhuma Fila Selecionada")
return
}
if (isMounted.current) setLoading(true);
const delayDebounceFn = setTimeout(() => {
const ticketCreate = async () => {
try {
const { data: ticket } = await api.post("/tickets", {
contactId: contactId,
userId: userId,
queueId: queueId,
status: "open",
});
history.push(`/tickets/${ticket.id}`);
} catch (err) {
toastError(err);
}
if (isMounted.current) setLoading(false);
};
ticketCreate();
}, 300);
return () => clearTimeout(delayDebounceFn);
}, [history])
useEffect(() => {
if (modalOpen && queues.length <= 1) {
handleSaveTicket(contactId, user.id, selectedQueue)
}
return () => {
isMounted.current = false;
};
}, [modalOpen, contactId, user.id, selectedQueue, handleSaveTicket, queues.length]);
if (modalOpen && queues.length <= 1) {
return <LinearProgress />
}
return (
<Dialog open={modalOpen} onClose={handleClose} maxWidth="xs" scroll="paper" classes={{ paper: classes.paper }}>
<DialogTitle id="form-dialog-title">
{i18n.t("newTicketModal.title")}
</DialogTitle>
<DialogContent dividers>
<FormControl variant="outlined" className={classes.maxWidth}>
<InputLabel>{i18n.t("Selecionar Fila")}</InputLabel>
<Select
value={selectedQueue}
onChange={(e) => setSelectedQueue(e.target.value)}
label={i18n.t("Filas")}
>
<MenuItem value={''}>&nbsp;</MenuItem>
{queues.map(({ id, color, name }) => (
<MenuItem
key={id}
value={id}
onMouseEnter={() => setItemHover(id)}
onMouseLeave={() => setItemHover(-1)}
style={{
background: id !== itemHover ? "white" : color,
}}
>{name[0].toUpperCase() + name.slice(1).toLowerCase()}</MenuItem>
))}
</Select>
</FormControl>
</DialogContent>
<DialogActions>
<Button
onClick={handleClose}
color="secondary"
disabled={loading}
variant="outlined"
>
{i18n.t("newTicketModal.buttons.cancel")}
</Button>
<ButtonWithSpinner
onClick={() => handleSaveTicket(contactId, user.id, selectedQueue)}
variant="contained"
color="primary"
loading={loading}
>
{i18n.t("newTicketModal.buttons.ok")}
</ButtonWithSpinner>
</DialogActions>
</Dialog>
);
};
export default ContactCreateTicketModal;

View File

@ -1,8 +1,9 @@
import React, { useState, useEffect, useReducer, useRef } from "react"; import React, { useContext, useState, useEffect, useReducer, useRef } from "react";
import { isSameDay, parseISO, format } from "date-fns"; import { isSameDay, parseISO, format } from "date-fns";
import openSocket from "socket.io-client"; import openSocket from "socket.io-client";
import clsx from "clsx"; import clsx from "clsx";
import { AuthContext } from "../../context/Auth/AuthContext";
import { green } from "@material-ui/core/colors"; import { green } from "@material-ui/core/colors";
import { import {
@ -318,6 +319,9 @@ const MessagesList = ({ ticketId, isGroup }) => {
const [anchorEl, setAnchorEl] = useState(null); const [anchorEl, setAnchorEl] = useState(null);
const messageOptionsMenuOpen = Boolean(anchorEl); const messageOptionsMenuOpen = Boolean(anchorEl);
const currentTicketId = useRef(ticketId); const currentTicketId = useRef(ticketId);
const [sendSeen, setSendSeen] = useState(false)
const { user } = useContext(AuthContext);
useEffect(() => { useEffect(() => {
dispatch({ type: "RESET" }); dispatch({ type: "RESET" });
@ -327,13 +331,77 @@ const MessagesList = ({ ticketId, isGroup }) => {
}, [ticketId]); }, [ticketId]);
useEffect(() => { useEffect(() => {
let url_split
let url_ticketId
try {
url_split = window.location.href.split('tickets')
url_ticketId = url_split[url_split.length - 1].match(/\d+/)[0]
} catch (error) {
console.log('error on try do the send seen: ', error)
}
if (!url_ticketId) return
if (!sendSeen) return
const delayDebounceFn = setTimeout(() => {
const sendSeenMessage = async () => {
try {
const { data } = await api.get("/messages/" + ticketId, {
params: { pageNumber },
});
setSendSeen(false)
if (!data) return
if (data.ticket.status === "open" && /*data.ticket.unreadMessages > 0 &&*/
data.ticket.userId === user.id) {
let fromMe = false
if (data.messages.length > 0) {
fromMe = data.messages[data.messages.length - 1].fromMe
}
// Atualiza Unread messages para 0
// Atualizei função no back-end para receber o novo parametro unreadMessages
const ticketUpdate = { ...data.ticket, unreadMessages: 0, fromMe }
await api.put("/tickets/" + ticketId, ticketUpdate)
}
} catch (err) {
setLoading(false);
toastError(err);
}
};
sendSeenMessage();
}, 500);
return () => {
clearTimeout(delayDebounceFn);
};
}, [sendSeen, pageNumber, ticketId, user.id]);
useEffect(() => {
setLoading(true); setLoading(true);
const delayDebounceFn = setTimeout(() => { const delayDebounceFn = setTimeout(() => {
const fetchMessages = async () => { const fetchMessages = async () => {
try { try {
const { data } = await api.get("/messages/" + ticketId, { const { data } = await api.get("/messages/" + ticketId, {
params: { pageNumber }, params: { pageNumber },
}); });
if (currentTicketId.current === ticketId) { if (currentTicketId.current === ticketId) {
dispatch({ type: "LOAD_MESSAGES", payload: data.messages }); dispatch({ type: "LOAD_MESSAGES", payload: data.messages });
@ -365,17 +433,12 @@ const MessagesList = ({ ticketId, isGroup }) => {
if (data.action === "create") { if (data.action === "create") {
console.log('ADD_MESSAGE: ', data.message)
dispatch({ type: "ADD_MESSAGE", payload: data.message }); dispatch({ type: "ADD_MESSAGE", payload: data.message });
scrollToBottom(); scrollToBottom();
} }
if (data.action === "update") { if (data.action === "update") {
console.log('joinChatBox update: ',data.action)
dispatch({ type: "UPDATE_MESSAGE", payload: data.message }); dispatch({ type: "UPDATE_MESSAGE", payload: data.message });
} }
}); });
@ -391,7 +454,11 @@ const MessagesList = ({ ticketId, isGroup }) => {
const scrollToBottom = () => { const scrollToBottom = () => {
if (lastMessageRef.current) { if (lastMessageRef.current) {
setSendSeen(true)
lastMessageRef.current.scrollIntoView({}); lastMessageRef.current.scrollIntoView({});
} }
}; };
@ -513,7 +580,7 @@ const MessagesList = ({ ticketId, isGroup }) => {
if (index === messagesList.length - 1) { if (index === messagesList.length - 1) {
let messageDay = parseISO(messagesList[index].createdAt); let messageDay = parseISO(messagesList[index].createdAt);
let previousMessageDay = parseISO(messagesList[index - 1].createdAt); let previousMessageDay = parseISO(messagesList[index - 1].createdAt);
return ( return (
<> <>
@ -682,4 +749,4 @@ const MessagesList = ({ ticketId, isGroup }) => {
); );
}; };
export default MessagesList; export default MessagesList;

View File

@ -116,17 +116,13 @@ const NotificationsPopOver = () => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL); const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
socket.on("reload_page", (data) => { socket.on("reload_page", (data) => {
console.log('UPDATING THE PAGE: ', data.userId, ' | user.id: ', user.id)
if (user.id === data.userId) { if (user.id === data.userId) {
window.location.reload(true); window.location.reload(true);
} }
}) })
@ -167,8 +163,7 @@ const NotificationsPopOver = () => {
clearInterval(_fifo); clearInterval(_fifo);
} }
_fifo = setInterval(() => { _fifo = setInterval(() => {
console.log('user.id: ', user.id)
socket.emit("online", user.id) socket.emit("online", user.id)
}, 3000); }, 3000);

View File

@ -11,7 +11,7 @@ import {
import { i18n } from "../../../translate/i18n"; // import { i18n } from "../../../translate/i18n";
import ptBrLocale from "date-fns/locale/pt-BR"; import ptBrLocale from "date-fns/locale/pt-BR";

View File

@ -83,14 +83,14 @@ const Ticket = () => {
const [contact, setContact] = useState({}); const [contact, setContact] = useState({});
const [ticket, setTicket] = useState({}); const [ticket, setTicket] = useState({});
const [statusChatEnd, setStatusChatEnd] = useState({}) const [statusChatEnd, setStatusChatEnd] = useState({})
useEffect(() => { useEffect(() => {
setLoading(true); setLoading(true);
const delayDebounceFn = setTimeout(() => { const delayDebounceFn = setTimeout(() => {
const fetchTicket = async () => { const fetchTicket = async () => {
try { try {
// maria julia // maria julia
const { data } = await api.get("/tickets/" + ticketId); const { data } = await api.get("/tickets/" + ticketId);
@ -101,7 +101,7 @@ const Ticket = () => {
setContact(data.contact.contact); setContact(data.contact.contact);
setTicket(data.contact); setTicket(data.contact);
setStatusChatEnd(data.statusChatEnd) setStatusChatEnd(data.statusChatEnd)
setLoading(false); setLoading(false);
} catch (err) { } catch (err) {
@ -171,8 +171,8 @@ const Ticket = () => {
onClick={handleDrawerOpen} onClick={handleDrawerOpen}
/> />
</div> </div>
<div className={classes.ticketActionButtons}> <div className={classes.ticketActionButtons}>
<TicketActionButtons ticket={ticket} statusChatEnd={statusChatEnd}/> <TicketActionButtons ticket={ticket} statusChatEnd={statusChatEnd} />
</div> </div>
</TicketHeader> </TicketHeader>
<ReplyMessageProvider> <ReplyMessageProvider>

View File

@ -29,12 +29,24 @@ const useStyles = makeStyles(theme => ({
}, },
})); }));
const TicketActionButtons = ({ ticket, statusChatEnd }) => { const TicketActionButtons = ({ ticket, statusChatEnd }) => {
const classes = useStyles(); const classes = useStyles();
const history = useHistory(); const history = useHistory();
const [anchorEl, setAnchorEl] = useState(null); const [anchorEl, setAnchorEl] = useState(null);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [useDialogflow, setUseDialogflow] = useState(ticket.contact.useDialogflow);
// const [useDialogflow, setUseDialogflow] = useState(ticket.contact.useDialogflow);
// const [/*useDialogflow*/, setUseDialogflow] = useState(() => {
// if (Object.keys(ticket).length !== 0) {
// return ticket.contact.useDialogflow;
// } else {
// // Set a default value if `ticket.contact.useDialogflow` is null
// return true
// }
// });
const ticketOptionsMenuOpen = Boolean(anchorEl); const ticketOptionsMenuOpen = Boolean(anchorEl);
const { user } = useContext(AuthContext); const { user } = useContext(AuthContext);
@ -53,9 +65,7 @@ const TicketActionButtons = ({ ticket, statusChatEnd }) => {
if (data) { if (data) {
data = { ...data, 'ticketId': ticket.id } data = { ...data, 'ticketId': ticket.id }
handleUpdateTicketStatus(null, "closed", user?.id, data) handleUpdateTicketStatus(null, "closed", user?.id, data)
@ -122,17 +132,17 @@ const TicketActionButtons = ({ ticket, statusChatEnd }) => {
}; };
const handleContactToggleUseDialogflow = async () => { // const handleContactToggleUseDialogflow = async () => {
setLoading(true); // setLoading(true);
try { // try {
const contact = await api.put(`/contacts/toggleUseDialogflow/${ticket.contact.id}`); // const contact = await api.put(`/contacts/toggleUseDialogflow/${ticket.contact.id}`);
setUseDialogflow(contact.data.useDialogflow); // setUseDialogflow(contact.data.useDialogflow);
setLoading(false); // setLoading(false);
} catch (err) { // } catch (err) {
setLoading(false); // setLoading(false);
toastError(err); // toastError(err);
} // }
}; // };
return ( return (

View File

@ -1,4 +1,6 @@
import React, { useState, useEffect, useReducer, useContext } from "react"; import React, { useState, useEffect, useReducer, useContext } from "react";
import openSocket from "socket.io-client"; import openSocket from "socket.io-client";
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from "@material-ui/core/styles";
@ -175,7 +177,7 @@ const reducer = (state, action) => {
}; };
const TicketsList = (props) => { const TicketsList = (props) => {
const { status, searchParam, showAll, selectedQueueIds, updateCount, style, tab } = props; const { status, searchParam, searchParamContent, showAll, selectedQueueIds, updateCount, style, tab } = props;
const classes = useStyles(); const classes = useStyles();
const [pageNumber, setPageNumber] = useState(1); const [pageNumber, setPageNumber] = useState(1);
const [ticketsList, dispatch] = useReducer(reducer, []); const [ticketsList, dispatch] = useReducer(reducer, []);
@ -184,16 +186,16 @@ const TicketsList = (props) => {
const { searchTicket } = useContext(SearchTicketContext) const { searchTicket } = useContext(SearchTicketContext)
useEffect(() => { useEffect(() => {
dispatch({ type: "RESET" }); dispatch({ type: "RESET" });
setPageNumber(1); setPageNumber(1);
}, [status, searchParam, showAll, selectedQueueIds, searchTicket]); }, [status, searchParam, searchParamContent, showAll, selectedQueueIds, searchTicket]);
const { tickets, hasMore, loading } = useTickets({ const { tickets, hasMore, loading } = useTickets({
pageNumber, pageNumber,
searchParam, searchParam,
searchParamContent,
status, status,
showAll, showAll,
queueIds: JSON.stringify(selectedQueueIds), queueIds: JSON.stringify(selectedQueueIds),
@ -201,6 +203,7 @@ const TicketsList = (props) => {
}); });
useEffect(() => { useEffect(() => {
if (!status && !searchParam) return; if (!status && !searchParam) return;
// if (searchParam) { // if (searchParam) {

View File

@ -1,14 +1,21 @@
import React, { useContext, useEffect, useRef, useState } from "react"; import React, { useContext, useEffect, useRef, useState } from "react";
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from "@material-ui/core/styles";
import { IconButton } from "@mui/material";
import Paper from "@material-ui/core/Paper"; import Paper from "@material-ui/core/Paper";
import SearchIcon from "@material-ui/icons/Search";
import InputBase from "@material-ui/core/InputBase"; import InputBase from "@material-ui/core/InputBase";
import Tabs from "@material-ui/core/Tabs"; import Tabs from "@material-ui/core/Tabs";
import Tab from "@material-ui/core/Tab"; import Tab from "@material-ui/core/Tab";
import Badge from "@material-ui/core/Badge"; import Badge from "@material-ui/core/Badge";
import Tooltip from "@material-ui/core/Tooltip";
import SearchIcon from "@material-ui/icons/Search";
import MoveToInboxIcon from "@material-ui/icons/MoveToInbox"; import MoveToInboxIcon from "@material-ui/icons/MoveToInbox";
import CheckBoxIcon from "@material-ui/icons/CheckBox"; import CheckBoxIcon from "@material-ui/icons/CheckBox";
import MenuIcon from "@material-ui/icons/Menu";
import FindInPageIcon from '@material-ui/icons/FindInPage';
import FormControlLabel from "@material-ui/core/FormControlLabel"; import FormControlLabel from "@material-ui/core/FormControlLabel";
import Switch from "@material-ui/core/Switch"; import Switch from "@material-ui/core/Switch";
@ -63,6 +70,25 @@ const useStyles = makeStyles((theme) => ({
}, },
serachInputWrapper: { serachInputWrapper: {
flex: 1,
display: "flex",
flexDirection: "column",
gap: "10px",
borderRadius: 40,
padding: 4,
marginRight: theme.spacing(1),
},
searchInputHeader: {
flex: 1,
background: "#fff",
display: "flex",
borderRadius: 40,
padding: 4,
marginRight: theme.spacing(1),
},
searchContentInput: {
flex: 1, flex: 1,
background: "#fff", background: "#fff",
display: "flex", display: "flex",
@ -78,6 +104,11 @@ const useStyles = makeStyles((theme) => ({
alignSelf: "center", alignSelf: "center",
}, },
menuSearch: {
color: "grey",
alignSelf: "center",
},
searchInput: { searchInput: {
flex: 1, flex: 1,
border: "none", border: "none",
@ -95,20 +126,21 @@ const useStyles = makeStyles((theme) => ({
}, },
})); }));
const DEFAULT_SEARCH_PARAM = { searchParam: "", searchParamContent: "" }
const TicketsManager = () => { const TicketsManager = () => {
const { tabOption, setTabOption } = useContext(TabTicketContext); const { tabOption, setTabOption } = useContext(TabTicketContext);
const {setSearchTicket} = useContext(SearchTicketContext) const { setSearchTicket } = useContext(SearchTicketContext)
const classes = useStyles(); const classes = useStyles();
const [searchParam, setSearchParam] = useState(""); const [searchParam, setSearchParam] = useState(DEFAULT_SEARCH_PARAM);
const [tab, setTab] = useState("open"); const [tab, setTab] = useState("open");
const [tabOpen, setTabOpen] = useState("open"); const [tabOpen, setTabOpen] = useState("open");
const [newTicketModalOpen, setNewTicketModalOpen] = useState(false); const [newTicketModalOpen, setNewTicketModalOpen] = useState(false);
const [showAllTickets, setShowAllTickets] = useState(false); const [showAllTickets, setShowAllTickets] = useState(false);
const searchInputRef = useRef();
const { user } = useContext(AuthContext); const { user } = useContext(AuthContext);
const [openCount, setOpenCount] = useState(0); const [openCount, setOpenCount] = useState(0);
@ -117,7 +149,16 @@ const TicketsManager = () => {
const userQueueIds = user.queues.map((q) => q.id); const userQueueIds = user.queues.map((q) => q.id);
const [selectedQueueIds, setSelectedQueueIds] = useState(userQueueIds || []); const [selectedQueueIds, setSelectedQueueIds] = useState(userQueueIds || []);
const [showContentSearch, setShowContentSearch] = useState(false)
const searchInputRef = useRef();
const searchContentInputRef = useRef();
const [inputSearch, setInputSearch] = useState(''); const [inputSearch, setInputSearch] = useState('');
const [inputContentSearch, setInputContentSearch] = useState("")
const [openTooltipSearch, setOpenTooltipSearch] = useState(false)
let searchTimeout;
let searchContentTimeout;
useEffect(() => { useEffect(() => {
if (user.profile.toUpperCase() === "ADMIN") { if (user.profile.toUpperCase() === "ADMIN") {
@ -135,22 +176,45 @@ const TicketsManager = () => {
}, [tab, setTabOption]); }, [tab, setTabOption]);
useEffect(() => {
// clearTimeout(searchContentTimeout);
// setSearchParam(prev => ({ ...prev, searchParamContent: "" }))
if (!inputContentSearch) return
if (!searchContentTimeout) return
// searchContentTimeout = setTimeout(() => {
// setSearchParam(prev => ({ ...prev, searchParamContent: inputContentSearch }));
// }, 500);
clearTimeout(searchContentTimeout);
setSearchParam(prev => ({ ...prev, searchParamContent: "" }))
}, [inputContentSearch, searchContentTimeout]);
useEffect(() => { useEffect(() => {
if (tabOption === 'open') { if (tabOption === 'open') {
setTabOption('') setTabOption('')
setSearchParam(''); setSearchParam(DEFAULT_SEARCH_PARAM);
setInputSearch(''); setInputSearch('');
setInputContentSearch('')
setTab("open"); setTab("open");
return; return;
} }
}, [tabOption, setTabOption]) }, [tabOption, setTabOption])
let searchTimeout;
const removeExtraSpace = (str) => { const removeExtraSpace = (str) => {
str = str.replace(/^\s+/g, '') str = str.replace(/^\s+/g, '')
@ -163,24 +227,58 @@ const TicketsManager = () => {
setInputSearch(removeExtraSpace(searchedTerm)) setInputSearch(removeExtraSpace(searchedTerm))
setSearchTicket(searchParam) setSearchTicket(searchParam.searchParam)
clearTimeout(searchTimeout); clearTimeout(searchTimeout);
if (searchedTerm === "") { if (searchedTerm === "") {
setSearchParam(searchedTerm); setSearchParam(prev => ({ ...prev, searchParam: searchedTerm }))
setInputSearch(searchedTerm) setInputSearch(searchedTerm)
setShowContentSearch(false)
setTab("open"); setTab("open");
return; return;
} }
if (searchedTerm.length < 4) {
setSearchParam(prev => ({ ...prev, searchParamContent: "" }))
setInputContentSearch('')
}
searchTimeout = setTimeout(() => { searchTimeout = setTimeout(() => {
setSearchParam(searchedTerm); setSearchParam(prev => ({ ...prev, searchParam: searchedTerm }));
}, 500); }, 500);
}; };
const handleContentSearch = e => {
let searchedContentText = removeExtraSpace(e.target.value.toLowerCase())
setInputContentSearch(searchedContentText)
searchContentTimeout = setTimeout(() => {
setSearchParam(prev => ({ ...prev, searchParamContent: searchedContentText }));
}, 500);
}
const handleOpenTooltipSearch = () => {
if (searchParam.searchParam.length < 4) {
setOpenTooltipSearch(true)
}
}
const handleCloseTooltipSearch = () => {
setOpenTooltipSearch(false)
if (searchParam.searchParam.length < 4) {
searchInputRef.current.focus()
}
}
const handleChangeTab = (e, newValue) => { const handleChangeTab = (e, newValue) => {
setTab(newValue); setTab(newValue);
}; };
@ -233,15 +331,50 @@ const TicketsManager = () => {
<Paper square elevation={0} className={classes.ticketOptionsBox}> <Paper square elevation={0} className={classes.ticketOptionsBox}>
{tab === "search" ? ( {tab === "search" ? (
<div className={classes.serachInputWrapper}> <div className={classes.serachInputWrapper}>
<SearchIcon className={classes.searchIcon} /> <div className={classes.searchInputHeader}>
<InputBase <SearchIcon className={classes.searchIcon} />
className={classes.searchInput} <InputBase
inputRef={searchInputRef} className={classes.searchInput}
placeholder={i18n.t("tickets.search.placeholder")} inputRef={searchInputRef}
type="search" placeholder={i18n.t("tickets.search.placeholder")}
value={inputSearch} type="search"
onChange={handleSearch} value={inputSearch}
/> onChange={handleSearch}
/>
{/* <IconButton onClick={() => setShowContentSearch(prev => !prev)}>
<MenuIcon className={classes.menuSearch} />
</IconButton> */}
<Tooltip
open={openTooltipSearch}
onOpen={() => handleOpenTooltipSearch()}
onClose={() => handleCloseTooltipSearch()}
title="Digite pelo menos 4 caracteres"
arrow>
<span>
<IconButton
disabled={searchParam.searchParam.length < 4}
onClick={() => setShowContentSearch(prev => !prev)}
>
<MenuIcon className={classes.menuSearch} />
</IconButton>
</span>
</Tooltip>
</div>
{
// showContentSearch ?
(showContentSearch && searchParam.searchParam.length >= 4) ?
(<div className={classes.searchContentInput}>
<FindInPageIcon className={classes.searchIcon} />
<InputBase
className={classes.searchInput}
inputRef={searchContentInputRef}
placeholder={i18n.t("Busca por conteúdo")}
type="search"
value={inputContentSearch}
onChange={(e) => handleContentSearch(e)}
/>
</div>) : null
}
</div> </div>
) : ( ) : (
<> <>
@ -340,17 +473,18 @@ const TicketsManager = () => {
</TabPanel> </TabPanel>
<TabPanel value={tab} name="search" className={classes.ticketsWrapper}> <TabPanel value={tab} name="search" className={classes.ticketsWrapper}>
<TicketsList <TicketsList
searchParam={searchParam} searchParam={searchParam.searchParam}
tab={tab} searchParamContent={searchParam.searchParamContent}
showAll={true} tab={tab}
selectedQueueIds={selectedQueueIds} showAll={true}
/> selectedQueueIds={selectedQueueIds}
/>
</TabPanel> </TabPanel>
</Paper> </Paper>
); );
}; };
export default TicketsManager; export default TicketsManager;

View File

@ -1,8 +1,7 @@
import React, { useState, useEffect } from "react"; import React, { useState, useContext, useMemo } from "react";
import { useHistory } from "react-router-dom"; import { useHistory } from "react-router-dom";
import Button from "@material-ui/core/Button"; import Button from "@material-ui/core/Button";
import TextField from "@material-ui/core/TextField";
import Dialog from "@material-ui/core/Dialog"; import Dialog from "@material-ui/core/Dialog";
import Select from "@material-ui/core/Select"; import Select from "@material-ui/core/Select";
import FormControl from "@material-ui/core/FormControl"; import FormControl from "@material-ui/core/FormControl";
@ -13,102 +12,72 @@ import { makeStyles } from "@material-ui/core";
import DialogActions from "@material-ui/core/DialogActions"; import DialogActions from "@material-ui/core/DialogActions";
import DialogContent from "@material-ui/core/DialogContent"; import DialogContent from "@material-ui/core/DialogContent";
import DialogTitle from "@material-ui/core/DialogTitle"; import DialogTitle from "@material-ui/core/DialogTitle";
import Autocomplete, {
createFilterOptions,
} from "@material-ui/lab/Autocomplete";
import CircularProgress from "@material-ui/core/CircularProgress";
import { i18n } from "../../translate/i18n"; import { i18n } from "../../translate/i18n";
import api from "../../services/api"; import api from "../../services/api";
import ButtonWithSpinner from "../ButtonWithSpinner"; import ButtonWithSpinner from "../ButtonWithSpinner";
import toastError from "../../errors/toastError"; import toastError from "../../errors/toastError";
import useQueues from "../../hooks/useQueues";
import { WhatsAppsContext } from "../../context/WhatsApp/WhatsAppsContext";
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
maxWidth: { maxWidth: {
width: "100%", width: "100%",
}, },
})); }));
const filterOptions = createFilterOptions({ // Receive array of queues arrays
trim: true, // Return a new array with unique queues from all arrays has passed by the parameter
}); const queueArraysToOneArray = (array) => {
if (!array) return []
const map = {}
const uniqueQueuesAvailable = []
array.forEach((queues) => {
queues.forEach(({ id, name, color }) => {
if (!map[id]) {
map[id] = true
uniqueQueuesAvailable.push({ id, name, color })
}
})
})
return uniqueQueuesAvailable
}
const TransferTicketModal = ({ modalOpen, onClose, ticketid }) => { const TransferTicketModal = ({ modalOpen, onClose, ticketid }) => {
const history = useHistory(); const history = useHistory();
const [options, setOptions] = useState([]); const { whatsApps } = useContext(WhatsAppsContext);
const [queues, setQueues] = useState([]);
const [allQueues, setAllQueues] = useState([]);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [searchParam, setSearchParam] = useState("");
const [selectedUser, setSelectedUser] = useState(null);
const [selectedQueue, setSelectedQueue] = useState(''); const [selectedQueue, setSelectedQueue] = useState('');
const classes = useStyles(); const classes = useStyles();
const { findAll: findAllQueues } = useQueues(); const queues = useMemo(() => {
if (!whatsApps) return []
useEffect(() => { const whatsAppsQueues = whatsApps.map(({ queues }) => queues)
const loadQueues = async () => { //const whatsAppsQueues = whatsApps.filter(({ status }) => status === "CONNECTED" ).map(({ queues }) => queues)
const list = await findAllQueues(); const uniqueQueuesAvailable = queueArraysToOneArray(whatsAppsQueues)
setAllQueues(list); return uniqueQueuesAvailable
setQueues(list); }, [whatsApps])
} const [itemHover, setItemHover] = useState(-1)
loadQueues();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
if (!modalOpen || searchParam.length < 3) {
setLoading(false);
return;
}
setLoading(true);
const delayDebounceFn = setTimeout(() => {
const fetchUsers = async () => {
try {
const { data } = await api.get("/users/", {
params: { searchParam },
});
setOptions(data.users);
setLoading(false);
} catch (err) {
setLoading(false);
toastError(err);
}
};
fetchUsers();
}, 500);
return () => clearTimeout(delayDebounceFn);
}, [searchParam, modalOpen]);
const handleClose = () => { const handleClose = () => {
onClose(); onClose();
setSearchParam("");
setSelectedUser(null);
}; };
const handleSaveTicket = async e => { const handleSaveTicket = async e => {
e.preventDefault(); e.preventDefault();
if (!ticketid) return; if (!ticketid) return;
if (!selectedQueue) return;
setLoading(true); setLoading(true);
try { try {
let data = {}; let data = {};
if (selectedUser) {
data.userId = selectedUser.id
}
if (selectedQueue && selectedQueue !== null) { if (selectedQueue && selectedQueue !== null) {
data.queueId = selectedQueue data.queueId = selectedQueue
if (!selectedUser) {
data.status = 'pending';
data.userId = null;
}
} }
// test del PARA APARECER NA FILA DE OUTRO ATENDENTE E O MESMO CLICAR EM ACEITAR AO INVES DE ENVIAR PARA ATENDENDO // test del PARA APARECER NA FILA DE OUTRO ATENDENTE E O MESMO CLICAR EM ACEITAR AO INVES DE ENVIAR PARA ATENDENDO
data.status = 'pending' data.status = 'pending'
data.transfer = true
await api.put(`/tickets/${ticketid}`, data); await api.put(`/tickets/${ticketid}`, data);
@ -127,56 +96,26 @@ const TransferTicketModal = ({ modalOpen, onClose, ticketid }) => {
{i18n.t("transferTicketModal.title")} {i18n.t("transferTicketModal.title")}
</DialogTitle> </DialogTitle>
<DialogContent dividers> <DialogContent dividers>
<Autocomplete
style={{ width: 300, marginBottom: 20 }}
getOptionLabel={option => `${option.name}`}
onChange={(e, newValue) => {
setSelectedUser(newValue);
if (newValue != null && Array.isArray(newValue.queues)) {
setQueues(newValue.queues);
} else {
setQueues(allQueues);
setSelectedQueue('');
}
}}
options={options}
filterOptions={filterOptions}
freeSolo
autoHighlight
noOptionsText={i18n.t("transferTicketModal.noOptions")}
loading={loading}
renderInput={params => (
<TextField
{...params}
label={i18n.t("transferTicketModal.fieldLabel")}
variant="outlined"
required
autoFocus
onChange={e => setSearchParam(e.target.value)}
InputProps={{
...params.InputProps,
endAdornment: (
<React.Fragment>
{loading ? (
<CircularProgress color="inherit" size={20} />
) : null}
{params.InputProps.endAdornment}
</React.Fragment>
),
}}
/>
)}
/>
<FormControl variant="outlined" className={classes.maxWidth}> <FormControl variant="outlined" className={classes.maxWidth}>
<InputLabel>{i18n.t("transferTicketModal.fieldQueueLabel")}</InputLabel> <InputLabel>{i18n.t("transferTicketModal.fieldQueueLabel")}</InputLabel>
<Select <Select
value={selectedQueue} value={selectedQueue}
onChange={(e) => setSelectedQueue(e.target.value)} onChange={(e) => setSelectedQueue(e.target.value)}
label={i18n.t("transferTicketModal.fieldQueuePlaceholder")} label={i18n.t("transferTicketModal.fieldQueuePlaceholder")}
required
> >
<MenuItem value={''}>&nbsp;</MenuItem> <MenuItem style={{ background: "white", }} value={''}>&nbsp;</MenuItem>
{queues.map((queue) => ( {queues.map((queue) => (
<MenuItem key={queue.id} value={queue.id}>{queue.name}</MenuItem> <MenuItem
key={queue.id}
value={queue.id}
onMouseEnter={() => setItemHover(queue.id)}
onMouseLeave={() => setItemHover(-1)}
style={{
background: queue.id !== itemHover ? "white" : queue.color,
}}
>{queue.name}
</MenuItem>
))} ))}
</Select> </Select>
</FormControl> </FormControl>
@ -200,8 +139,8 @@ const TransferTicketModal = ({ modalOpen, onClose, ticketid }) => {
</ButtonWithSpinner> </ButtonWithSpinner>
</DialogActions> </DialogActions>
</form> </form>
</Dialog> </Dialog >
); );
}; };
export default TransferTicketModal; export default TransferTicketModal;

View File

@ -5,6 +5,7 @@ import api from "../../services/api";
const useTickets = ({ const useTickets = ({
searchParam, searchParam,
searchParamContent,
pageNumber, pageNumber,
status, status,
date, date,
@ -35,6 +36,7 @@ const useTickets = ({
const { data } = await api.get("/tickets", { const { data } = await api.get("/tickets", {
params: { params: {
searchParam, searchParam,
searchParamContent,
pageNumber, pageNumber,
status, status,
date, date,
@ -63,6 +65,7 @@ const useTickets = ({
return () => clearTimeout(delayDebounceFn); return () => clearTimeout(delayDebounceFn);
}, [ }, [
searchParam, searchParam,
searchParamContent,
pageNumber, pageNumber,
status, status,
date, date,

View File

@ -37,8 +37,9 @@ import { Can } from "../../components/Can";
import apiBroker from "../../services/apiBroker"; import apiBroker from "../../services/apiBroker";
import fileDownload from 'js-file-download' import fileDownload from 'js-file-download'
import ContactCreateTicketModal from "../../components/ContactCreateTicketModal";
const reducer = (state, action) => { const reducer = (state, action) => {
@ -111,6 +112,7 @@ const Contacts = () => {
const [contacts, dispatch] = useReducer(reducer, []); const [contacts, dispatch] = useReducer(reducer, []);
const [selectedContactId, setSelectedContactId] = useState(null); const [selectedContactId, setSelectedContactId] = useState(null);
const [contactModalOpen, setContactModalOpen] = useState(false); const [contactModalOpen, setContactModalOpen] = useState(false);
const [isCreateTicketModalOpen, setIsCreateTicketModalOpen] = useState(false)
const [deletingContact, setDeletingContact] = useState(null); const [deletingContact, setDeletingContact] = useState(null);
const [confirmOpen, setConfirmOpen] = useState(false); const [confirmOpen, setConfirmOpen] = useState(false);
const [hasMore, setHasMore] = useState(false); const [hasMore, setHasMore] = useState(false);
@ -118,17 +120,17 @@ const Contacts = () => {
const [onQueueStatus, setOnQueueProcessStatus] = useState(undefined) const [onQueueStatus, setOnQueueProcessStatus] = useState(undefined)
const [zipfile, setZipFile] = useState() const [zipfile, setZipFile] = useState()
async function handleChange(event) { async function handleChange(event) {
try { try {
if (event.target.files[0].size > 1024 * 1024 * 4){ if (event.target.files[0].size > 1024 * 1024 * 4) {
alert('Arquivo não pode ser maior que 4 MB!') alert('Arquivo não pode ser maior que 4 MB!')
return return
} }
const formData = new FormData(); const formData = new FormData();
formData.append("adminId", user.id); formData.append("adminId", user.id);
@ -302,21 +304,30 @@ const Contacts = () => {
setContactModalOpen(false); setContactModalOpen(false);
}; };
const handleSaveTicket = async (contactId) => { const handleOpenCreateTicketModal = (contactId) => {
if (!contactId) return; setSelectedContactId(contactId)
setLoading(true); setIsCreateTicketModalOpen(true)
try { }
const { data: ticket } = await api.post("/tickets", {
contactId: contactId, const handleCloseCreateTicketModal = () => {
userId: user?.id, setIsCreateTicketModalOpen(false)
status: "open", }
});
history.push(`/tickets/${ticket.id}`); // const handleSaveTicket = async (contactId) => {
} catch (err) { // if (!contactId) return;
toastError(err); // setLoading(true);
} // try {
setLoading(false); // 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) => { const hadleEditContact = (contactId) => {
setSelectedContactId(contactId); setSelectedContactId(contactId);
@ -415,21 +426,21 @@ const Contacts = () => {
switch (param) { switch (param) {
case 'empty': case 'empty':
return ( return (
<> <>
<input <input
type="file" type="file"
accept=".csv" accept=".csv"
style={{ display: 'none' }} style={{ display: 'none' }}
onChange={handleChange} onChange={handleChange}
id="contained-button-file" id="contained-button-file"
/> />
<label htmlFor="contained-button-file"> <label htmlFor="contained-button-file">
<Button variant="contained" color="primary" component="span"> <Button variant="contained" color="primary" component="span">
CSV UPLOAD CSV UPLOAD
</Button> </Button>
</label> </label>
{/* <Button {/* <Button
disabled={query && query.length > 0 ? false : true} disabled={query && query.length > 0 ? false : true}
@ -495,6 +506,11 @@ const Contacts = () => {
aria-labelledby="form-dialog-title" aria-labelledby="form-dialog-title"
contactId={selectedContactId} contactId={selectedContactId}
></ContactModal> ></ContactModal>
<ContactCreateTicketModal
modalOpen={isCreateTicketModalOpen}
onClose={handleCloseCreateTicketModal}
contactId={selectedContactId}
/>
<ConfirmationModal <ConfirmationModal
title={ title={
deletingContact deletingContact
@ -609,7 +625,7 @@ const Contacts = () => {
<TableCell align="center"> <TableCell align="center">
<IconButton <IconButton
size="small" size="small"
onClick={() => handleSaveTicket(contact.id)} onClick={() => handleOpenCreateTicketModal(contact.id)}
> >
<WhatsAppIcon /> <WhatsAppIcon />
</IconButton> </IconButton>
@ -646,4 +662,4 @@ const Contacts = () => {
); );
}; };
export default Contacts; export default Contacts;

View File

@ -16,7 +16,7 @@ const useLoadData = (setLoading, dispatch, route, dispatchType) => {
setLoading(false); setLoading(false);
} }
})(); })();
}, []); }, [setLoading, dispatch, route, dispatchType]);
} }
export default useLoadData; export default useLoadData;

View File

@ -18,7 +18,7 @@ const useSocket = (dispatch, socketEvent, typeUpdate, typeDelete, payloadUpdate,
return () => { return () => {
socket.disconnect(); socket.disconnect();
}; };
}, []); }, [dispatch, socketEvent, typeUpdate, typeDelete, payloadUpdate, payloadDelete]);
} }
export default useSocket; export default useSocket;