refactor: code adjustment
parent
a1195810fe
commit
ce94c22f0a
|
@ -0,0 +1,208 @@
|
||||||
|
{
|
||||||
|
"authentication": {
|
||||||
|
"type": "oauth2",
|
||||||
|
"crmClientId": "3MVG9JJwBBbcN47Kv0Z7EuNd19INI1Bhe7uX_Wz6M0VlMyWJD4xPKTtn_b39bGn6LmdSkKJ.aLNGdV1brj16C",
|
||||||
|
"crmClientSecret": "870E8D51A1CA06896D966A3D92ABD885346DAD4428926E965776C479055969E7",
|
||||||
|
"crmScopes": "full refresh_token",
|
||||||
|
"crmPhoneTest": "5514983253326"
|
||||||
|
},
|
||||||
|
"crmRest": [
|
||||||
|
{
|
||||||
|
"authorizationEndpoint": {
|
||||||
|
"request": {
|
||||||
|
"requestContentType": "empty",
|
||||||
|
"requestEncoding": "empty",
|
||||||
|
"requestType": "Get",
|
||||||
|
"responseType": "empty",
|
||||||
|
"url": "https://login.salesforce.com/services/oauth2/authorize?response_type=code&client_id=crmClientId&code_challenge=bDXEJ0wxr0s369lGxHwewLULiOuyl6Y3W7QZABmn2S4&redirect_uri=crmRedirectURI&scope=crmScopes&code_challenge_method=S256"
|
||||||
|
},
|
||||||
|
"body": {},
|
||||||
|
"response": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tokenEndpoint": {
|
||||||
|
"request": {
|
||||||
|
"requestContentType": "none",
|
||||||
|
"requestEncoding": "Json",
|
||||||
|
"requestType": "Post",
|
||||||
|
"responseType": "Json",
|
||||||
|
"url": "https://login.salesforce.com/services/oauth2/token"
|
||||||
|
},
|
||||||
|
"body": {},
|
||||||
|
"response": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"createContactRecord": {
|
||||||
|
"request": {
|
||||||
|
"requestContentType": "application/json",
|
||||||
|
"requestEncoding": "Json",
|
||||||
|
"requestType": "Post",
|
||||||
|
"responseType": "Json",
|
||||||
|
"url": "https://nocompany-a9-dev-ed.develop.my.salesforce.com/services/data/v61.0/sobjects/Contact"
|
||||||
|
},
|
||||||
|
"body": {
|
||||||
|
"Phone": "crmPhone",
|
||||||
|
"LastName": "crmLastName",
|
||||||
|
"FirstName": "crmFirstName"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"id": "id"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lookupContactByPhone": {
|
||||||
|
"request": {
|
||||||
|
"requestContentType": "application/json",
|
||||||
|
"requestEncoding": "Json",
|
||||||
|
"requestType": "Get",
|
||||||
|
"responseType": "Json",
|
||||||
|
"url": "https://nocompany-a9-dev-ed.develop.my.salesforce.com/services/data/v61.0/query/?q=SELECT+Id,+Phone,+AccountId+FROM+Contact+WHERE+Phone='crmPhone'"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"phone": "records[0].Phone",
|
||||||
|
"id": "records[0].Id",
|
||||||
|
"accountId": "records[0].AccountId",
|
||||||
|
"name": "records[0].Name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"callJournaling": {
|
||||||
|
"request": {
|
||||||
|
"requestContentType": "application/json",
|
||||||
|
"requestEncoding": "Json",
|
||||||
|
"requestType": "Post",
|
||||||
|
"responseType": "Json",
|
||||||
|
"url": "https://nocompany-a9-dev-ed.develop.my.salesforce.com/services/data/v61.0/sobjects/Task"
|
||||||
|
},
|
||||||
|
"calls": [
|
||||||
|
{
|
||||||
|
"inboundAnsweredCall": {
|
||||||
|
"Subject": "Call Journal",
|
||||||
|
"WhoId": "crmContactId",
|
||||||
|
"Description": "Ligação recebida",
|
||||||
|
"Status": "Completed",
|
||||||
|
"Priority": "Normal",
|
||||||
|
"CallType": "Outbound",
|
||||||
|
"CallDurationInSeconds": {
|
||||||
|
"_prop": "crmCallDuration",
|
||||||
|
"_type": "number",
|
||||||
|
"_format": "seconds"
|
||||||
|
},
|
||||||
|
"ActivityDate": "YYYY-MM-DD",
|
||||||
|
"TaskSubtype": "Call"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inboundMissedCall": {
|
||||||
|
"Subject": "Call Journal",
|
||||||
|
"WhoId": "crmContactId",
|
||||||
|
"Description": "Ligação recebida perdida",
|
||||||
|
"Status": "Completed",
|
||||||
|
"Priority": "Normal",
|
||||||
|
"CallType": "Outbound",
|
||||||
|
"CallDurationInSeconds": {
|
||||||
|
"_prop": "crmCallDuration",
|
||||||
|
"_type": "number",
|
||||||
|
"_format": "seconds"
|
||||||
|
},
|
||||||
|
"ActivityDate": "YYYY-MM-DD",
|
||||||
|
"TaskSubtype": "Call"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"outboundAnsweredCall": {
|
||||||
|
"Subject": "Call Journal",
|
||||||
|
"WhoId": "crmContactId",
|
||||||
|
"Description": "Ligação realizada",
|
||||||
|
"Status": "Completed",
|
||||||
|
"Priority": "Normal",
|
||||||
|
"CallType": "Outbound",
|
||||||
|
"CallDurationInSeconds": {
|
||||||
|
"_prop": "crmCallDuration",
|
||||||
|
"_type": "number",
|
||||||
|
"_format": "seconds"
|
||||||
|
},
|
||||||
|
"ActivityDate": "YYYY-MM-DD",
|
||||||
|
"TaskSubtype": "Call"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"outboundUnansweredCall": {
|
||||||
|
"Subject": "Call Journal",
|
||||||
|
"WhoId": "crmContactId",
|
||||||
|
"Description": "Ligação realizada nao atendida",
|
||||||
|
"Status": "Completed",
|
||||||
|
"Priority": "Normal",
|
||||||
|
"CallType": "Outbound",
|
||||||
|
"CallDurationInSeconds": {
|
||||||
|
"_prop": "crmCallDuration",
|
||||||
|
"_type": "number",
|
||||||
|
"_format": "seconds"
|
||||||
|
},
|
||||||
|
"ActivityDate": "YYYY-MM-DD",
|
||||||
|
"TaskSubtype": "Call"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"response": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chatJournaling": {
|
||||||
|
"request": {
|
||||||
|
"requestContentType": "application/json",
|
||||||
|
"requestEncoding": "Json",
|
||||||
|
"requestType": "Post",
|
||||||
|
"responseType": "Json",
|
||||||
|
"url": "https://nocompany-a9-dev-ed.develop.my.salesforce.com/services/data/v61.0/sobjects/Task"
|
||||||
|
},
|
||||||
|
"chats": [
|
||||||
|
{
|
||||||
|
"chatDone":{
|
||||||
|
"WhoId": "crmContactId",
|
||||||
|
"Subject": "WhatsApp Chat",
|
||||||
|
"Description": "Conversation started via WhatsApp. Conversation link: chatLink",
|
||||||
|
"ActivityDate": "YYYY-MM-DD",
|
||||||
|
"Status": "Completed",
|
||||||
|
"Priority": "Normal"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"redirectLink": {
|
||||||
|
"request": {
|
||||||
|
"url": "https://nocompany-a9-dev-ed.develop.lightning.force.com/lightning/r/Lead/crmContactId/edit?count=1&backgroundContext=%2Flightning%2Fr%2FLead%2F00Qak0000098YFhEAM%2Fview"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"createCase": {
|
||||||
|
"request": {
|
||||||
|
"requestContentType": "application/json",
|
||||||
|
"requestEncoding": "Json",
|
||||||
|
"requestType": "Post",
|
||||||
|
"responseType": "Json",
|
||||||
|
"url": "https://nocompany-a9-dev-ed.develop.my.salesforce.com/services/data/v61.0/sobjects/Case"
|
||||||
|
},
|
||||||
|
"body": {
|
||||||
|
|
||||||
|
"Subject": "Assunto do Caso teste",
|
||||||
|
"Description": "Descrição detalhada do caso test",
|
||||||
|
"Status": "Novo",
|
||||||
|
"Priority": "Alta",
|
||||||
|
"Departamento__c": "Financeiro"
|
||||||
|
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"id": "id"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -408,7 +408,7 @@ const createTicket = async (req, res) => {
|
||||||
const ticketIdMatch = crmTicketLink.ticketId.match(/ticket\/(\d+)/);
|
const ticketIdMatch = crmTicketLink.ticketId.match(/ticket\/(\d+)/);
|
||||||
if (ticketIdMatch && ticketIdMatch[1]) {
|
if (ticketIdMatch && ticketIdMatch[1]) {
|
||||||
const ticketId = ticketIdMatch[1];
|
const ticketId = ticketIdMatch[1];
|
||||||
await set(crmPhone, ticketId);
|
await set(crmPhone, ticketId, 10800); // excluido após 3 horas(10800 segundos) caso não receba a requisição de trascrição
|
||||||
console.log(`TicketId ${ticketId} para crmPhone ${crmPhone} salvo no Redis com sucesso.`);
|
console.log(`TicketId ${ticketId} para crmPhone ${crmPhone} salvo no Redis com sucesso.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -557,41 +557,6 @@ const webhook_crm = async (req, res) => {
|
||||||
return res.set('Content-Type', 'text/xml').status(StatusCodes.OK).send(responseXml)
|
return res.set('Content-Type', 'text/xml').status(StatusCodes.OK).send(responseXml)
|
||||||
}
|
}
|
||||||
|
|
||||||
const associateTicketToCaller = async (req, res) => {
|
|
||||||
try {
|
|
||||||
const { callerId, ticketId } = req.body;
|
|
||||||
|
|
||||||
if (!callerId || !ticketId) {
|
|
||||||
return res.status(StatusCodes.BAD_REQUEST).json({
|
|
||||||
error: 'Campos obrigatórios ausentes. É necessário fornecer callerId e ticketId.'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove o zero inicial do número se existir
|
|
||||||
const formattedCallerId = removeZeroInicial(callerId);
|
|
||||||
|
|
||||||
// Adiciona o prefixo 55 se não existir
|
|
||||||
const fullCallerId = formattedCallerId.startsWith('55') ? formattedCallerId : `55${formattedCallerId}`;
|
|
||||||
|
|
||||||
// Salva a associação no Redis com um tempo de expiração de 5 minutos (300 segundos)
|
|
||||||
await set(fullCallerId, ticketId, 300);
|
|
||||||
|
|
||||||
console.log(`Ticket ${ticketId} associado ao número ${fullCallerId} com sucesso.`);
|
|
||||||
|
|
||||||
return res.status(StatusCodes.OK).json({
|
|
||||||
message: 'Ticket associado com sucesso',
|
|
||||||
callerId: fullCallerId,
|
|
||||||
ticketId
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Erro ao associar ticket:', error);
|
|
||||||
Sentry.captureException(error);
|
|
||||||
return res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
|
|
||||||
error: 'Erro ao processar a associação do ticket'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const checkTicketByCrmPhone = async (req, res) => {
|
const checkTicketByCrmPhone = async (req, res) => {
|
||||||
const { crmPhone } = req.query;
|
const { crmPhone } = req.query;
|
||||||
|
@ -633,7 +598,6 @@ module.exports = {
|
||||||
sfCreateCase,
|
sfCreateCase,
|
||||||
sfUpdateCase,
|
sfUpdateCase,
|
||||||
createTicket,
|
createTicket,
|
||||||
associateTicketToCaller,
|
|
||||||
checkTicketByCrmPhone
|
checkTicketByCrmPhone
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,55 +1,50 @@
|
||||||
const redis = require('redis');
|
|
||||||
const HubspotService = require('../services/hubspotService');
|
const HubspotService = require('../services/hubspotService');
|
||||||
require('dotenv').config();
|
const { get, del } = require('../utils/redisClient')
|
||||||
|
|
||||||
const redisClient = redis.createClient({
|
|
||||||
url: process.env.REDIS_URI
|
|
||||||
});
|
|
||||||
|
|
||||||
redisClient.on('error', (err) => console.log('Redis Client Error', err));
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
if (!redisClient.isOpen) {
|
|
||||||
await redisClient.connect();
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
const hubspotService = new HubspotService();
|
const hubspotService = new HubspotService();
|
||||||
|
|
||||||
exports.receiveTranscription = async (req, res) => {
|
const receiveTranscription = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { callerId, uniqueId, transcription, recordingUrl } = req.body;
|
|
||||||
|
|
||||||
if (!callerId || !uniqueId || !transcription || !recordingUrl) {
|
const { crmPhone, uniqueId, transcription, recordingUrl, companyId } = req.body;
|
||||||
|
|
||||||
|
if (!crmPhone || !uniqueId || !transcription || !recordingUrl) {
|
||||||
return res.status(400).json({ error: 'Campos obrigatórios ausentes.' });
|
return res.status(400).json({ error: 'Campos obrigatórios ausentes.' });
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Recebida transcrição para callerId: ${callerId}, uniqueId: ${uniqueId}`);
|
console.log(`Recebida transcrição para crmPhone: ${crmPhone}, uniqueId: ${uniqueId}`);
|
||||||
console.log('Transcrição:', transcription.summary);
|
console.log('Transcrição:', transcription.summary);
|
||||||
|
|
||||||
// 1. Buscar ticketId no Redis
|
// 1. Buscar ticketId no Redis
|
||||||
const ticketId = await redisClient.get(callerId);
|
const ticketId = await get(crmPhone);
|
||||||
|
|
||||||
if (!ticketId) {
|
if (!ticketId) {
|
||||||
console.warn(`Nenhum ticketId encontrado no Redis para o callerId: ${callerId}. A transcrição será salva como uma nota sem associação ao ticket.`);
|
console.warn(`Nenhum ticketId encontrado no Redis para o crmPhone: ${crmPhone}. A transcrição será salva como uma nota sem associação ao ticket.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Buscar ou criar contato no HubSpot
|
// 2. Buscar ou criar contato no HubSpot
|
||||||
const contact = await hubspotService.createContactIfNotExists(callerId);
|
const contact = await hubspotService.createContactIfNotExists(companyId, crmPhone);
|
||||||
|
|
||||||
// 3. Criar nota no HubSpot e associar ao contato e ao ticket (se existir)
|
// 3. Criar nota no HubSpot e associar ao contato e ao ticket (se existir)
|
||||||
await hubspotService.createCallNote(contact.id, {
|
await hubspotService.createCallNote(contact.contactId, {
|
||||||
transcription: `${transcription.client || ''}\n${transcription.agent || ''}`,
|
transcription: `${transcription.client || ''}\n${transcription.agent || ''}`,
|
||||||
summary: transcription.summary,
|
summary: transcription.summary,
|
||||||
recordingUrl,
|
recordingUrl,
|
||||||
callerId,
|
crmPhone,
|
||||||
uniqueId,
|
uniqueId,
|
||||||
ticketId
|
ticketId
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await del(crmPhone)
|
||||||
|
|
||||||
return res.status(200).json({ message: 'Transcrição recebida e processada com sucesso!' });
|
return res.status(200).json({ message: 'Transcrição recebida e processada com sucesso!' });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Erro ao receber transcrição:', error?.response?.data || error.message || error);
|
console.error('Erro ao receber transcrição:', error?.response?.data || error.message || error);
|
||||||
return res.status(500).json({ error: 'Erro ao processar a transcrição.' });
|
return res.status(500).json({ error: 'Erro ao processar a transcrição.' });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
module.exports ={
|
||||||
|
receiveTranscription
|
||||||
|
}
|
|
@ -4,7 +4,6 @@ const { authorization, } = require('../middleware/authentication')
|
||||||
const { contactCreate, sfCreateCase, sfUpdateCase, createTicket, testTemplate, webhook_crm, uploadCrmConfig, callJournaling, oauthCallBack, install, deleteCrm, deleteCompany, getCrms, webhook, checkTicketByCrmPhone } = require('../controllers/crmController')
|
const { contactCreate, sfCreateCase, sfUpdateCase, createTicket, testTemplate, webhook_crm, uploadCrmConfig, callJournaling, oauthCallBack, install, deleteCrm, deleteCompany, getCrms, webhook, checkTicketByCrmPhone } = require('../controllers/crmController')
|
||||||
const { receiveTranscription } = require('../controllers/transcriptionController')
|
const { receiveTranscription } = require('../controllers/transcriptionController')
|
||||||
const { fileUpload } = require("../utils")
|
const { fileUpload } = require("../utils")
|
||||||
const { associateTicketToCaller } = require('../controllers/crmController');
|
|
||||||
|
|
||||||
router.route('/create-contact').post(authorization, contactCreate)
|
router.route('/create-contact').post(authorization, contactCreate)
|
||||||
router.route('/create-ticket').post(authorization, createTicket)
|
router.route('/create-ticket').post(authorization, createTicket)
|
||||||
|
@ -19,7 +18,7 @@ router.route('/install').get(install)
|
||||||
router.route('/test').post(testTemplate)
|
router.route('/test').post(testTemplate)
|
||||||
router.route('/webhook').post(webhook)
|
router.route('/webhook').post(webhook)
|
||||||
router.route('/webhook-crm').post(webhook_crm)
|
router.route('/webhook-crm').post(webhook_crm)
|
||||||
router.route('/transcriptions').post( receiveTranscription)
|
router.route('/transcriptions').post(authorization, receiveTranscription)
|
||||||
router.route('/:companyId').get(authorization, getCrms)
|
router.route('/:companyId').get(authorization, getCrms)
|
||||||
router.route('/tickets/check-by-crmphone').get(authorization, checkTicketByCrmPhone)
|
router.route('/tickets/check-by-crmphone').get(authorization, checkTicketByCrmPhone)
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
const axios = require('axios');
|
const axios = require('axios');
|
||||||
const Logger = console;
|
const Logger = console;
|
||||||
require('dotenv').config();
|
require('dotenv').config();
|
||||||
|
const loadCRM = require('../utils/loadCRM')
|
||||||
|
const lookupContactByPhone = require('../utils/lookupCRMContactByPhone')
|
||||||
|
const createContact = require('../utils/createContact')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serviço para integração com a API do HubSpot.
|
* Serviço para integração com a API do HubSpot.
|
||||||
|
@ -112,22 +115,26 @@ Link da Gravação: ${recordingUrl}
|
||||||
/**
|
/**
|
||||||
* Cria um contato com o número informado caso não exista.
|
* Cria um contato com o número informado caso não exista.
|
||||||
*
|
*
|
||||||
* @param {string} phoneNumber - Número de telefone do contato.
|
* @param {string} crmPhone - Número de telefone do contato.
|
||||||
* @returns {Promise<Object>} - Contato existente ou novo contato criado.
|
* @returns {Promise<Object>} - Contato existente ou novo contato criado.
|
||||||
*/
|
*/
|
||||||
async createContactIfNotExists(phoneNumber) {
|
async createContactIfNotExists(companyId, crmPhone) {
|
||||||
const existing = await this.findContactByPhone(phoneNumber);
|
|
||||||
if (existing) return existing;
|
|
||||||
|
|
||||||
const response = await this.client.post('/crm/v3/objects/contacts', {
|
const crmFiles = await loadCRM(companyId)
|
||||||
properties: {
|
|
||||||
phone: phoneNumber,
|
|
||||||
|
|
||||||
|
if (crmFiles.length > 0) {
|
||||||
|
|
||||||
|
const { crmRest: rest, authentication } = crmFiles[0].crm
|
||||||
|
|
||||||
|
let contact = await lookupContactByPhone(rest, authentication, crmPhone, companyId)
|
||||||
|
|
||||||
|
if (!contact.exist) {
|
||||||
|
contact = await createContact(companyId, rest, authentication, crmPhone)
|
||||||
|
}
|
||||||
|
|
||||||
|
return contact
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
this.logger.log(`Contato criado para o número ${phoneNumber}`);
|
|
||||||
return response.data;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue