diff --git a/backend/src/helpers/BotIsOnQueue.ts b/backend/src/helpers/BotIsOnQueue.ts new file mode 100644 index 0000000..beb8747 --- /dev/null +++ b/backend/src/helpers/BotIsOnQueue.ts @@ -0,0 +1,37 @@ +const fsPromises = require("fs/promises"); +const fs = require('fs') + +import ListUsersService from "../services/UserServices/ListUsersService" + +const _botIsOnQueue = async (botName: string) => { + + const { users, count, hasMore } = await ListUsersService({searchParam:`${botName}`,pageNumber:1}); + let botIsOnQueue = false + let userIdBot = null + let queueId = null + + if(users.length > 0){ + + try { + + console.log('----------------- bot queue id: ', Object(users)[0]["queues"][0].id) + queueId = Object(users)[0]["queues"][0].id; + userIdBot = Object(users)[0].id + botIsOnQueue = true + + }catch(err){ + + console.log('O usuário botqueue não está em nenhuma fila err: ',err) + + } + + } + else{ + console.log('Usuário botqueue não existe!') + } + + return { userIdBot: userIdBot, botQueueId: queueId, isOnQueue: botIsOnQueue } + + } + + export default _botIsOnQueue; \ No newline at end of file diff --git a/backend/src/services/TicketServices/FindOrCreateTicketService.ts b/backend/src/services/TicketServices/FindOrCreateTicketService.ts index bb5ed2c..14396c8 100644 --- a/backend/src/services/TicketServices/FindOrCreateTicketService.ts +++ b/backend/src/services/TicketServices/FindOrCreateTicketService.ts @@ -50,7 +50,10 @@ const FindOrCreateTicketService = async ( //[Op.between]: [+subHours(new Date(), 2), +new Date()] // Tempo osioso para a ura responder thuanny - [Op.between]: [+subMinutes(new Date(), 30), +new Date()] + //[Op.between]: [+subMinutes(new Date(), 30), +new Date()] + + // Sub seconds + [Op.between]: [+subSeconds(new Date(), 3), +new Date()] }, contactId: contact.id }, diff --git a/backend/src/services/TicketServices/ShowTicketMessage.ts b/backend/src/services/TicketServices/ShowTicketMessage.ts new file mode 100644 index 0000000..d0af812 --- /dev/null +++ b/backend/src/services/TicketServices/ShowTicketMessage.ts @@ -0,0 +1,61 @@ +import Ticket from "../../models/Ticket"; +import AppError from "../../errors/AppError"; +import Contact from "../../models/Contact"; +import User from "../../models/User"; +import Queue from "../../models/Queue"; + +import Message from "../../models/Message"; +import { userInfo } from "os"; + +import { Op, where } from "sequelize"; + +import { Sequelize } from "sequelize"; +import moment from 'moment'; + +import { startOfDay, endOfDay, parseISO, getDate} from "date-fns"; +import { string } from "yup/lib/locale"; + +//Report by user, startDate, endDate +const ShowTicketMessage = async (ticketId: string | number, onlyNumber: boolean = false, limit?: number, regexp?: string): Promise => { + + let where_clause = {} + + if(onlyNumber){ + where_clause = { + ticketId: ticketId, + fromMe: 0, + //body: {[Op.regexp]: '^[0-9]*$'}, + // body: {[Op.regexp]: '^[0-3]$'}, + body: {[Op.regexp]: regexp}, + } + } + else{ + where_clause = { + ticketId: ticketId, + fromMe: 0, + } + } + + + const ticket = await Message.findAll({ + + where: where_clause , + limit: limit ? limit : 10000, + raw:true, + attributes: ['body', 'read', 'mediaType','fromMe', 'mediaUrl', [Sequelize.fn("DATE_FORMAT",Sequelize.col("createdAt"),"%d/%m/%Y %H:%i:%s"),"createdAt"]], + + order: [ + ['createdAt', 'DESC'] + ] + + }); + + + if (!ticket) { + throw new AppError("ERR_NO_TICKET_FOUND", 404); + } + + return ticket; +}; + +export default ShowTicketMessage; diff --git a/backend/src/services/WbotServices/ura_placity _msg_transfer.ts b/backend/src/services/WbotServices/ura_placity _msg_transfer.ts new file mode 100644 index 0000000..16fc8fd --- /dev/null +++ b/backend/src/services/WbotServices/ura_placity _msg_transfer.ts @@ -0,0 +1,7 @@ + + const msg_client_transfer = + { + "msg":"Seu atendimento foi transferido!\nEm breve você será atendido por um de nossos atendentes." + } + +export default msg_client_transfer; \ No newline at end of file diff --git a/backend/src/services/WbotServices/ura_placity.ts b/backend/src/services/WbotServices/ura_placity.ts new file mode 100644 index 0000000..89b7b43 --- /dev/null +++ b/backend/src/services/WbotServices/ura_placity.ts @@ -0,0 +1,60 @@ + + const data = [ + { + "id":"1", + "option":"Tecle 1 para informações sobre horários de funcionamento", + "description":"1 - Play City Shopping Nova América, Play City Bangu Shopping e Play City Grande Rio, Terça a sexta de 18h às 22h, sábados, domingos e feriados de 16h às 22h", + "atendente":false + }, + { + "id":"2", + "option":"Tecle 2 para saber os endereços das unidades Play City", + "description":"2 - Play City Shopping Nova América:\n\nAv. Pastor Martin Luther King Júnior, 126 Del Castilho, Rio de Janeiro\n\n- Play City Bang Shopping:\nR. Fonseca, 240 - Bangu, Rio de Janeiro\n\n- Play City Grande Rio:\n\nRua Maria Sendas,111 - Parque Barreto, São João de Meriti.", + "atendente":false + }, + { + "id":"3", + "option":"Tecle 3 para informações sobre ingressos e passaportes", + "description":"3 - Informamos que a venda de Passaportes está ativa somente na Unidade Play City do Shopping Nova América (TERÇA A SEXTA, EXCETO FERIADO) e Bangu Shopping (TERÇA A DOMINGO).\n\nCompre seu passaporte na bilheteria com valor de meia entrada por R$70,00 ou compre de forma antecipada com desconto pelo site e pague R$59,90.\n\n- Ingressos individuais para todas as unidades nas bilheterias por R$8,00.\n\nAs cartelas com 5 ingressos custam R$35,00 e cartelas com 10 ingressos R$60 ou compre sua cartela com 10 ingressos de forma antecipada com desconto pelo site e pague R$49,90. Válido somente de terça a sexta para as 3 unidades.\n\nPara garantir sua cartela com 10 ingressos individuais para fins de semana e feriados, compre pelo site e pague apenas R$54,90. Válido para as 3 unidades Play City.\n\nwww.playcitydiversoes.com.br", + "atendente":false + }, + { + "id":"4", + "option":"Tecle 4 para cancelamento", + "description":"4 - Para cancelar uma compra envie um email para sac@playcitydiversoes.com.br as seguintes informações:\n1) O nome completo da pessoa que fez a compra.\n2) Número do pedido idêntico ao voucher\n3) Para qual unidade (Nova América, Grande Rio ou Bangu Shopping).\n4) Qual ingresso você comprou (cartela ou passaporte) e quantidade comprada\n5) Dia de semana, ou fim de semana e feriado.\n6) valor exato pago.\nOBS: Somente com todas as informações idênticas ao voucher poderão ter o cancelamento concluído com sucesso.", + "atendente":false + }, + { + "id":"5", + "option":"Tecle 5 para promoção de aniversariante do dia", + "description":"5 - Aniversariante do dia comprando 20 ingressos na bilheteria com apresentação de documento com foto paga R$120,00 e ganha mais 10 ingressos para curtir de montão!\n\nEsta opção é válida nas 3 unidades Play City.\n\nVocê também pode comprar 1 passaporte e ganhar mais 1! Esta opção é válida somente na unidade Shopping Nova América\n\nPROMOÇÕES NÃO ACUMULATIVAS COM OUTRAS PROMOÇÕES.\n\nOBS: Promoção de aniversarinte de passaporte apenas na unidade do Shopping Nova America(Terça a sexta)\n\n", + "atendente":false + }, + { + "id":"6", + "option":"Tecle 6 para saber regras de filmagem ou fotografia com equipamento profissional no parque", + "description":"6 - Para filmar ou fotografar com equipamento profissional nos espaços e brinquedos do parque é necessária a autorização prévia. Você pode solicitar e agendar pelo email Marketing@playcitydiversoes.com.br", + "atendente":false + }, + { + "id":"7", + "option":"Tecle 7 para parcerias comerciais", + "description":"7 - ENVIE UM EMAIL PARA MARKETING@PLAYCITYDIVERSOES.COM.BR", + "atendente":false + }, + { + "id":"8", + "option":"Tecle 8 informações do Espaço de Festa Play City", + "description":"8 - Se você quer comemorar em um espaço exclusivo e privativo, oferecemos a TENDA DA ALEGRIA! Nela você conta com 16 passaportes, 2 conjuntos de mesa com 8 cadeiras, 4 pufs, 1 mesa grande para bolo, 1 backdrop e você pode decorar como quiser!!!\n\nDe terça a sexta R$649,90 a vista ou 3x no cartão.\n\nSábados, domingos e feriados R$799,90 a vista ou 3x no cartão.\n\nO Espaço TENDA DA ALEGRIA é válido somente para a unidade Play City do Shopping Nova América e Bangu Shopping.\n\nTemos também a opção PREMIUM para você quer uma festa completa!!!! Nela você terá direito a 21 passaportes, espaço climatizado, 6 mesas com 24 cadeiras, 1 mesa grande para bolo, 1 mini freezer e 1 forno elétrico e você pode decorar como quiser!!!\n\nDe terça a sexta R$999,90 a vista ou 3x no cartão.\n\nSábados, domingos e feriados R$1499,90 a vista ou 3x no cartão.\n\nO Espaço PREMIUM é válido somente para a unidade Play City do Shopping Nova América.\n\nPromoções não cumulativas e com taxas de cancelamento.\n\nPara RESERVAR o espaço ( APERTE 9 PARA FALAR COM UM ATENDENTE)", + "atendente":false + }, + { + "id":"9", + "option":"Tecle 9 para falar com um de nossos atendentes", + "description":"", + "atendente":true + } +] + + +export default data; \ No newline at end of file diff --git a/backend/src/services/WbotServices/wbotMessageListener.ts b/backend/src/services/WbotServices/wbotMessageListener.ts index f8b1fcc..23f2cf4 100644 --- a/backend/src/services/WbotServices/wbotMessageListener.ts +++ b/backend/src/services/WbotServices/wbotMessageListener.ts @@ -22,7 +22,20 @@ import FindOrCreateTicketService from "../TicketServices/FindOrCreateTicketServi import ShowWhatsAppService from "../WhatsappService/ShowWhatsAppService"; import { debounce } from "../../helpers/Debounce"; import UpdateTicketService from "../TicketServices/UpdateTicketService"; -import { date } from "faker"; +import { date } from "faker"; + +import ShowQueueService from "../QueueService/ShowQueueService"; +import ShowTicketMessage from "../TicketServices/ShowTicketMessage" +import BotIsOnQueue from "../../helpers/BotIsOnQueue" +import Queue from "../../models/Queue"; + +import fs from 'fs'; + +// test del +import data_ura from './ura_placity' +import msg_client_transfer from './ura_placity _msg_transfer' +// + interface Session extends Client { @@ -133,6 +146,8 @@ const verifyMessage = async ( await CreateMessageService({ messageData }); }; + + const verifyQueue = async ( wbot: Session, @@ -141,20 +156,32 @@ const verifyQueue = async ( contact: Contact ) => { - const { queues, greetingMessage } = await ShowWhatsAppService(wbot.id!); - + const { queues, greetingMessage } = await ShowWhatsAppService(wbot.id!); + /*if (queues.length === 1) { await UpdateTicketService({ ticketData: { queueId: queues[0].id }, ticketId: ticket.id }); - return; + return; }*/ + + let selectedOption = null; - let choosenQueue = null + let choosenQueue = null + - if (queues.length === 1) { + const botInfo = await BotIsOnQueue('botqueue') + + + if(botInfo.isOnQueue){ + + choosenQueue = await ShowQueueService(botInfo.botQueueId); + + } + + else if (queues.length === 1) { selectedOption = 1; choosenQueue = queues[+selectedOption - 1]; } @@ -168,16 +195,28 @@ const verifyQueue = async ( choosenQueue = queues[+selectedOption - 1]; } - - // const selectedOption = msg.body; - // const choosenQueue = queues[+selectedOption - 1]; + + if (choosenQueue) { + await UpdateTicketService({ ticketData: { queueId: choosenQueue.id }, ticketId: ticket.id }); + + // O bot abre a mensagem na fila para atender o usuario + if(botInfo.isOnQueue){ + + await UpdateTicketService({ + ticketData:{ status: 'open', userId: botInfo.userIdBot }, + ticketId: ticket.id + }); + + } + // + const body = `\u200e${choosenQueue.greetingMessage}`; const sentMessage = await wbot.sendMessage(`${contact.number}@c.us`, body); @@ -221,6 +260,46 @@ const isValidMsg = (msg: WbotMessage): boolean => { return false; }; + +const queuesOutBot =async (wbot:Session, botId: string | number) => { + + const { queues, greetingMessage } = await ShowWhatsAppService(wbot.id!); + + const indexQueue = queues.findIndex((q) => q.id == botId) + + if(indexQueue != -1){ + queues.splice(indexQueue, 1) + } + + return {queues, greetingMessage} + +} + +const botTransferTicket = async (queues: Queue, ticket: Ticket, contact: Contact, wbot: Session) =>{ + + await ticket.update({ userId: null }); + + await UpdateTicketService({ ticketData: { status: 'pending', queueId: queues.id }, ticketId: ticket.id}); + +} + + +const botSendMessage = (ticket:Ticket, contact:Contact, wbot: Session, msg: string) => { + + const debouncedSentMessage = debounce( + + async () => { + const sentMessage = await wbot.sendMessage(`${contact.number}@c.us`, `${msg}`); + verifyMessage(sentMessage, ticket, contact); + }, + 3000, + ticket.id + ); + + debouncedSentMessage(); + +} + const handleMessage = async ( msg: WbotMessage, wbot: Session @@ -247,7 +326,9 @@ const handleMessage = async ( msgContact = await msg.getContact(); - console.log('-----msgContact TESTE MSG: ', msgContact, ' | msg.body: ', msg.body) + //console.log('-----msgContact TESTE MSG: ', msgContact, ' | msg.body: ', msg.body) + + console.log('-----msgContact TESTE MSG2: ', msgContact, ' | msg: ', msg) } @@ -300,6 +381,166 @@ const handleMessage = async ( 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 + + const botInfo = await BotIsOnQueue('botqueue') + + if( botInfo.isOnQueue && !msg.fromMe && ticket.userId == botInfo.userIdBot){ + + + // test del + if(msg.body === '0'){ + + const queue = await ShowQueueService(ticket.queue.id); + + const greetingMessage = `\u200e${queue.greetingMessage}`; + + botSendMessage(ticket, contact, wbot, `${greetingMessage}`) + + } + else{ + + + // Pega as ultimas 9 opções numericas digitadas pelo cliente em orde DESC + // Consulta apenas mensagens do usuári + + + let lastOption = '' + + let ura_length = data_ura.length + + let indexAttendant = data_ura.findIndex((u) => u.atendente ) + + let opt_user_attendant = '' + + if(indexAttendant != -1){ + opt_user_attendant = data_ura[indexAttendant].id + } + + let ticket_message = await ShowTicketMessage(ticket.id, true, ura_length, `^[0-${ura_length}}]$`); + + if(ticket_message.length > 1){ + + lastOption = ticket_message[1].body + + // test del + + const queuesWhatsGreetingMessage = await queuesOutBot(wbot, botInfo.botQueueId) + + const queues = queuesWhatsGreetingMessage.queues + + if(queues.length > 1){ + + const index_opt_user_attendant = ticket_message.findIndex((q) => q.body == opt_user_attendant) + const index0 = ticket_message.findIndex((q) => q.body == '0') + + if(index_opt_user_attendant != -1){ + + if(index0 > -1 && index0 < index_opt_user_attendant){ + lastOption = '' + } + else{ + lastOption = opt_user_attendant + } + } + + } + + // + + } + + + // È numero + if( !Number.isNaN(Number(msg.body.trim())) && (+msg.body >= 0 && +msg.body <= data_ura.length) ) { + + const indexUra = data_ura.findIndex((ura) => ura.id == msg.body.trim()) + + if(indexUra != -1){ + + if(data_ura[indexUra].id != opt_user_attendant && lastOption != opt_user_attendant){ + + botSendMessage(ticket, contact, wbot, `${data_ura[indexUra].description}\n\n *0* - Voltar ao menu principal`) + + } + else if(data_ura[indexUra].id == opt_user_attendant){ + + const queuesWhatsGreetingMessage = await queuesOutBot(wbot, botInfo.botQueueId) + + const queues = queuesWhatsGreetingMessage.queues + + // Se fila for maior que 1 exibi as opções fila para atendimento humano + if(queues.length > 1){ + + let options = ""; + + queues.forEach((queue, index) => { + + options += `*${index + 1}* - ${queue.name}\n`; + + }); + + const body = `\u200eSelecione uma das opções de atendimento abaixo:\n${options}`; + + botSendMessage(ticket, contact, wbot, body) + + } // Para situações onde há apenas uma fila com exclusão da fila do bot, já direciona o cliente para essa fila de atendimento humano + else if(queues.length == 1){ + + await botTransferTicket(queues[0], ticket, contact, wbot) + + botSendMessage(ticket, contact, wbot, `${msg_client_transfer.msg}`) + + } + } + else if (lastOption == opt_user_attendant){ + + const queuesWhatsGreetingMessage = await queuesOutBot(wbot, botInfo.botQueueId) + + const queues = queuesWhatsGreetingMessage.queues + + // É numero + if( !Number.isNaN(Number(msg.body.trim())) && (+msg.body >= 0 && +msg.body <= queues.length) ) { + + await botTransferTicket(queues[+msg.body - 1], ticket, contact, wbot) + + botSendMessage(ticket, contact, wbot, `${msg_client_transfer.msg}`) + } + else{ + + botSendMessage(ticket, contact, wbot, `Digite um número válido disponível no menu de opções de atendimento\n\n*0* - Voltar ao menu principal`) + + } + } + + + } + + } + else{ + + // É numero + if(!Number.isNaN(Number(msg.body.trim()))){ + + botSendMessage(ticket, contact, wbot, `Opção numérica inválida!\nDigite um dos números mostrados no menu de opções\n\n*0* - Voltar ao menu principal`) + + } + else{ + + botSendMessage(ticket, contact, wbot, `Digite um número válido disponível no menu de opções\n\n*0* - Voltar ao menu principal`) + + } + + } + + } + + + } + // + } catch (err) { Sentry.captureException(err);