Merge a1195810fe into f05bfa4047
				
					
				
			
						commit
						47baefd047
					
				|  | @ -1,4 +1,3 @@ | |||
| 
 | ||||
| const path = require('path') | ||||
| const getIntegrationsConfig = require('../utils/getIntegrationsConfig') | ||||
| 
 | ||||
|  | @ -388,6 +387,16 @@ const getCrms = async (req, res) => { | |||
|     res.status(StatusCodes.OK).send(crms) | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Cria um ou mais tickets para um cliente no CRM e armazena o `ticketId` no Redis associado ao `crmPhone`. | ||||
|  *  | ||||
|  * @param {import('express').Request} req - Objeto da requisição HTTP contendo `companyId` e `crmPhone` no corpo. | ||||
|  * @param {import('express').Response} res - Objeto da resposta HTTP. | ||||
|  *  | ||||
|  * @returns {Promise<import('express').Response>} - Resposta HTTP com os links dos tickets criados. | ||||
|  *  | ||||
|  * @throws {Error} - Lança erro em caso de falha na criação dos tickets ou requisição ao CRM. | ||||
|  */ | ||||
| const createTicket = async (req, res) => { | ||||
| 
 | ||||
|     let { companyId, crmPhone } = req.body | ||||
|  | @ -405,10 +414,18 @@ const createTicket = async (req, res) => { | |||
|             throw new Error(`Error on create ticket: companyID ${companyId} | crmPhone: ${crmPhone}`) | ||||
|         }) | ||||
| 
 | ||||
|     for (const crmTicketLink of crmTicketLinks) { | ||||
|         const ticketIdMatch = crmTicketLink.ticketId.match(/ticket\/(\d+)/); | ||||
|         if (ticketIdMatch && ticketIdMatch[1]) { | ||||
|             const ticketId = ticketIdMatch[1]; | ||||
|             await set(crmPhone, ticketId);  | ||||
|             console.log(`TicketId ${ticketId} para crmPhone ${crmPhone} salvo no Redis com sucesso.`); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return res.status(StatusCodes.OK).json({ crmTicketLinks }) | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| const webhook = async (req, res) => { | ||||
| 
 | ||||
|     const originIP = req.headers['x-forwarded-for'] || req.connection.remoteAddress | ||||
|  | @ -555,6 +572,67 @@ const webhook_crm = async (req, res) => { | |||
|     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 { crmPhone } = req.query; | ||||
| 
 | ||||
|     if (!crmPhone) { | ||||
|         return res.status(StatusCodes.BAD_REQUEST).json({ | ||||
|             error: 'O parâmetro crmPhone é obrigatório.' | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     try { | ||||
|         const ticketId = await get(crmPhone); | ||||
| 
 | ||||
|         if (ticketId) { | ||||
|             return res.status(StatusCodes.OK).json({ hasTicket: true, ticketId: ticketId }); | ||||
|         } else { | ||||
|             return res.status(StatusCodes.OK).json({ hasTicket: false }); | ||||
|         } | ||||
|     } catch (error) { | ||||
|         console.error('Erro ao verificar ticket no Redis:', error); | ||||
|         return res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({ | ||||
|             error: 'Erro interno ao verificar a existência do ticket.' | ||||
|         }); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| module.exports = { | ||||
|     contactCreate, | ||||
|     uploadCrmConfig, | ||||
|  | @ -569,7 +647,9 @@ module.exports = { | |||
|     webhook_crm, | ||||
|     sfCreateCase, | ||||
|     sfUpdateCase, | ||||
|     createTicket | ||||
|     createTicket, | ||||
|     associateTicketToCaller, | ||||
|     checkTicketByCrmPhone | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,55 @@ | |||
| const redis = require('redis'); | ||||
| const HubspotService = require('../services/hubspotService'); | ||||
| require('dotenv').config(); | ||||
| 
 | ||||
| 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(); | ||||
| 
 | ||||
| exports.receiveTranscription = async (req, res) => { | ||||
|   try { | ||||
|     const { callerId, uniqueId, transcription, recordingUrl } = req.body; | ||||
| 
 | ||||
|     if (!callerId || !uniqueId || !transcription || !recordingUrl) { | ||||
|       return res.status(400).json({ error: 'Campos obrigatórios ausentes.' }); | ||||
|     } | ||||
| 
 | ||||
|     console.log(`Recebida transcrição para callerId: ${callerId}, uniqueId: ${uniqueId}`); | ||||
|     console.log('Transcrição:', transcription.summary); | ||||
| 
 | ||||
|     // 1. Buscar ticketId no Redis
 | ||||
|     const ticketId = await redisClient.get(callerId); | ||||
| 
 | ||||
|     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.`); | ||||
|     } | ||||
| 
 | ||||
|     // 2. Buscar ou criar contato no HubSpot
 | ||||
|     const contact = await hubspotService.createContactIfNotExists(callerId); | ||||
| 
 | ||||
|     // 3. Criar nota no HubSpot e associar ao contato e ao ticket (se existir)
 | ||||
|     await hubspotService.createCallNote(contact.id, { | ||||
|       transcription: `${transcription.client || ''}\n${transcription.agent || ''}`, | ||||
|       summary: transcription.summary, | ||||
|       recordingUrl, | ||||
|       callerId, | ||||
|       uniqueId, | ||||
|       ticketId | ||||
|     }); | ||||
| 
 | ||||
|     return res.status(200).json({ message: 'Transcrição recebida e processada com sucesso!' }); | ||||
|   } catch (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.' }); | ||||
|   } | ||||
| }; | ||||
|  | @ -0,0 +1,16 @@ | |||
| PORT=6004 | ||||
| TOKEN=2ivck10D3o9qAZi0pkKudVDl9bdEVXY2s8gdxZ0jYgL1DZWTgDz6wDiIjlWgYmJtVOoqf0b42ZTLBRrfo8WoAaScRsujz3jQUNXdchSg0o43YilZGmVhheGJNAeIQRknHEll4nRJ7avcFgmDGoYbEey7TSC8EHS4Z3gzeufYYSfnKNDBwwzBURIQrTOxYFe3tBHsGOzwnuD2lU5tnEx7tr2XRO4zRNYeNY4lMBOFM0mRuyAe4kuqTrKXmJ8As200 | ||||
| URL_OAUTH_CALLBACK=http://localhost:6001/api/v1/crm/oauth-callback | ||||
| URL_OAUTH_FRONTEND_SUCCESS_REDIRECT=http://localhost:3000 | ||||
| DB_MONGO_URL=mongodb://localhost:27017 | ||||
| DB_MONGO_NAME=crm | ||||
| 
 | ||||
|  REDIS_URI=redis://127.0.0.1:6379 | ||||
| 
 | ||||
| IS_DEV=true | ||||
| REACT_APP_COMPANY_ID=1 | ||||
| REACT_APP_URL_API=http://localhost:6001 | ||||
| URL_HITPHONE_FRONTEND=https://ms-teamsapp.omnihit.app.br | ||||
| 
 | ||||
| # api hubspot  | ||||
| HUBSPOT_API_KEY=pat-na1-37da6668-e0b1-44cb-bd2d-596f5f65634a | ||||
|  | @ -9,6 +9,7 @@ | |||
|       "version": "1.0.0", | ||||
|       "license": "ISC", | ||||
|       "dependencies": { | ||||
|         "@hubspot/api-client": "^13.0.0", | ||||
|         "@sentry/node": "^9.9.0", | ||||
|         "@sentry/profiling-node": "^9.9.0", | ||||
|         "axios": "^1.6.1", | ||||
|  | @ -27,6 +28,7 @@ | |||
|         "mongoose": "^7.3.1", | ||||
|         "morgan": "^1.10.0", | ||||
|         "multer": "^1.4.5-lts.1", | ||||
|         "redis": "^5.5.6", | ||||
|         "socket.io": "^4.7.2", | ||||
|         "swagger-ui-express": "^4.1.6", | ||||
|         "xss-clean": "^0.1.1", | ||||
|  | @ -36,6 +38,24 @@ | |||
|         "nodemon": "^2.0.9" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@hubspot/api-client": { | ||||
|       "version": "13.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/@hubspot/api-client/-/api-client-13.0.0.tgz", | ||||
|       "integrity": "sha512-sXC1kRna+2uFlct1E1IQJQi53X36k24jRctmTTKzlvUM8it5VHXt6PCjVskQ4faLYGPyx9xh9GNx0GI7RMY4CQ==", | ||||
|       "license": "ISC", | ||||
|       "dependencies": { | ||||
|         "@types/node": "*", | ||||
|         "@types/node-fetch": "^2.5.7", | ||||
|         "bottleneck": "^2.19.5", | ||||
|         "es6-promise": "^4.2.4", | ||||
|         "form-data": "^2.5.0", | ||||
|         "lodash.merge": "^4.6.2", | ||||
|         "node-fetch": "^2.6.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=18.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@ioredis/commands": { | ||||
|       "version": "1.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", | ||||
|  | @ -591,6 +611,66 @@ | |||
|         "@opentelemetry/api": "^1.8" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@redis/bloom": { | ||||
|       "version": "5.5.6", | ||||
|       "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.5.6.tgz", | ||||
|       "integrity": "sha512-bNR3mxkwtfuCxNOzfV8B3R5zA1LiN57EH6zK4jVBIgzMzliNuReZXBFGnXvsi80/SYohajn78YdpYI+XNpqL+A==", | ||||
|       "license": "MIT", | ||||
|       "engines": { | ||||
|         "node": ">= 18" | ||||
|       }, | ||||
|       "peerDependencies": { | ||||
|         "@redis/client": "^5.5.6" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@redis/client": { | ||||
|       "version": "5.5.6", | ||||
|       "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.5.6.tgz", | ||||
|       "integrity": "sha512-M3Svdwt6oSfyfQdqEr0L2HOJH2vK7GgCFx1NfAQvpWAT4+ljoT1L5S5cKT3dA9NJrxrOPDkdoTPWJnIrGCOcmw==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "cluster-key-slot": "1.1.2" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">= 18" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@redis/json": { | ||||
|       "version": "5.5.6", | ||||
|       "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.5.6.tgz", | ||||
|       "integrity": "sha512-AIsoe3SsGQagqAmSQHaqxEinm5oCWr7zxPWL90kKaEdLJ+zw8KBznf2i9oK0WUFP5pFssSQUXqnscQKe2amfDQ==", | ||||
|       "license": "MIT", | ||||
|       "engines": { | ||||
|         "node": ">= 18" | ||||
|       }, | ||||
|       "peerDependencies": { | ||||
|         "@redis/client": "^5.5.6" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@redis/search": { | ||||
|       "version": "5.5.6", | ||||
|       "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.5.6.tgz", | ||||
|       "integrity": "sha512-JSqasYqO0mVcHL7oxvbySRBBZYRYhFl3W7f0Da7BW8M/r0Z9wCiVrdjnN4/mKBpWZkoJT/iuisLUdPGhpKxBew==", | ||||
|       "license": "MIT", | ||||
|       "engines": { | ||||
|         "node": ">= 18" | ||||
|       }, | ||||
|       "peerDependencies": { | ||||
|         "@redis/client": "^5.5.6" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@redis/time-series": { | ||||
|       "version": "5.5.6", | ||||
|       "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.5.6.tgz", | ||||
|       "integrity": "sha512-jkpcgq3NOI3TX7xEAJ3JgesJTxAx7k0m6lNxNsYdEM8KOl+xj7GaB/0CbLkoricZDmFSEAz7ClA1iK9XkGHf+Q==", | ||||
|       "license": "MIT", | ||||
|       "engines": { | ||||
|         "node": ">= 18" | ||||
|       }, | ||||
|       "peerDependencies": { | ||||
|         "@redis/client": "^5.5.6" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@sentry-internal/node-cpu-profiler": { | ||||
|       "version": "2.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/@sentry-internal/node-cpu-profiler/-/node-cpu-profiler-2.1.0.tgz", | ||||
|  | @ -751,6 +831,32 @@ | |||
|       "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.1.tgz", | ||||
|       "integrity": "sha512-4tT2UrL5LBqDwoed9wZ6N3umC4Yhz3W3FloMmiiG4JwmUJWpie0c7lcnUNd4gtMKuDEO4wRVS8B6Xa0uMRsMKg==" | ||||
|     }, | ||||
|     "node_modules/@types/node-fetch": { | ||||
|       "version": "2.6.12", | ||||
|       "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", | ||||
|       "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@types/node": "*", | ||||
|         "form-data": "^4.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/node-fetch/node_modules/form-data": { | ||||
|       "version": "4.0.3", | ||||
|       "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", | ||||
|       "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "asynckit": "^0.4.0", | ||||
|         "combined-stream": "^1.0.8", | ||||
|         "es-set-tostringtag": "^2.1.0", | ||||
|         "hasown": "^2.0.2", | ||||
|         "mime-types": "^2.1.12" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">= 6" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/pg": { | ||||
|       "version": "8.6.1", | ||||
|       "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.6.1.tgz", | ||||
|  | @ -1031,6 +1137,12 @@ | |||
|         "node": ">=10" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/bottleneck": { | ||||
|       "version": "2.19.5", | ||||
|       "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", | ||||
|       "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", | ||||
|       "license": "MIT" | ||||
|     }, | ||||
|     "node_modules/boxen": { | ||||
|       "version": "4.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", | ||||
|  | @ -1137,6 +1249,19 @@ | |||
|         "node": ">=8" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/call-bind-apply-helpers": { | ||||
|       "version": "1.0.2", | ||||
|       "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", | ||||
|       "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "es-errors": "^1.3.0", | ||||
|         "function-bind": "^1.1.2" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">= 0.4" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/camelcase": { | ||||
|       "version": "5.3.1", | ||||
|       "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", | ||||
|  | @ -1478,6 +1603,20 @@ | |||
|         "node": ">=10" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/dunder-proto": { | ||||
|       "version": "1.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", | ||||
|       "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "call-bind-apply-helpers": "^1.0.1", | ||||
|         "es-errors": "^1.3.0", | ||||
|         "gopd": "^1.2.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">= 0.4" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/duplexer3": { | ||||
|       "version": "0.1.4", | ||||
|       "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", | ||||
|  | @ -1569,6 +1708,57 @@ | |||
|       "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", | ||||
|       "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" | ||||
|     }, | ||||
|     "node_modules/es-define-property": { | ||||
|       "version": "1.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", | ||||
|       "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", | ||||
|       "license": "MIT", | ||||
|       "engines": { | ||||
|         "node": ">= 0.4" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/es-errors": { | ||||
|       "version": "1.3.0", | ||||
|       "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", | ||||
|       "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", | ||||
|       "license": "MIT", | ||||
|       "engines": { | ||||
|         "node": ">= 0.4" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/es-object-atoms": { | ||||
|       "version": "1.1.1", | ||||
|       "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", | ||||
|       "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "es-errors": "^1.3.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">= 0.4" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/es-set-tostringtag": { | ||||
|       "version": "2.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", | ||||
|       "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "es-errors": "^1.3.0", | ||||
|         "get-intrinsic": "^1.2.6", | ||||
|         "has-tostringtag": "^1.0.2", | ||||
|         "hasown": "^2.0.2" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">= 0.4" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/es6-promise": { | ||||
|       "version": "4.2.8", | ||||
|       "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", | ||||
|       "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", | ||||
|       "license": "MIT" | ||||
|     }, | ||||
|     "node_modules/escape-goat": { | ||||
|       "version": "2.1.1", | ||||
|       "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", | ||||
|  | @ -1753,6 +1943,42 @@ | |||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/form-data": { | ||||
|       "version": "2.5.3", | ||||
|       "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.3.tgz", | ||||
|       "integrity": "sha512-XHIrMD0NpDrNM/Ckf7XJiBbLl57KEhT3+i3yY+eWm+cqYZJQTZrKo8Y8AWKnuV5GT4scfuUGt9LzNoIx3dU1nQ==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "asynckit": "^0.4.0", | ||||
|         "combined-stream": "^1.0.8", | ||||
|         "es-set-tostringtag": "^2.1.0", | ||||
|         "mime-types": "^2.1.35", | ||||
|         "safe-buffer": "^5.2.1" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">= 0.12" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/form-data/node_modules/safe-buffer": { | ||||
|       "version": "5.2.1", | ||||
|       "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", | ||||
|       "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", | ||||
|       "funding": [ | ||||
|         { | ||||
|           "type": "github", | ||||
|           "url": "https://github.com/sponsors/feross" | ||||
|         }, | ||||
|         { | ||||
|           "type": "patreon", | ||||
|           "url": "https://www.patreon.com/feross" | ||||
|         }, | ||||
|         { | ||||
|           "type": "consulting", | ||||
|           "url": "https://feross.org/support" | ||||
|         } | ||||
|       ], | ||||
|       "license": "MIT" | ||||
|     }, | ||||
|     "node_modules/forwarded": { | ||||
|       "version": "0.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", | ||||
|  | @ -1787,6 +2013,43 @@ | |||
|         "url": "https://github.com/sponsors/ljharb" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/get-intrinsic": { | ||||
|       "version": "1.3.0", | ||||
|       "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", | ||||
|       "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "call-bind-apply-helpers": "^1.0.2", | ||||
|         "es-define-property": "^1.0.1", | ||||
|         "es-errors": "^1.3.0", | ||||
|         "es-object-atoms": "^1.1.1", | ||||
|         "function-bind": "^1.1.2", | ||||
|         "get-proto": "^1.0.1", | ||||
|         "gopd": "^1.2.0", | ||||
|         "has-symbols": "^1.1.0", | ||||
|         "hasown": "^2.0.2", | ||||
|         "math-intrinsics": "^1.1.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">= 0.4" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "url": "https://github.com/sponsors/ljharb" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/get-proto": { | ||||
|       "version": "1.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", | ||||
|       "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "dunder-proto": "^1.0.1", | ||||
|         "es-object-atoms": "^1.0.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">= 0.4" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/get-stream": { | ||||
|       "version": "4.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", | ||||
|  | @ -1845,6 +2108,18 @@ | |||
|         "url": "https://github.com/sponsors/sindresorhus" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/gopd": { | ||||
|       "version": "1.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", | ||||
|       "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", | ||||
|       "license": "MIT", | ||||
|       "engines": { | ||||
|         "node": ">= 0.4" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "url": "https://github.com/sponsors/ljharb" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/got": { | ||||
|       "version": "9.6.0", | ||||
|       "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", | ||||
|  | @ -1882,6 +2157,33 @@ | |||
|         "node": ">=4" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/has-symbols": { | ||||
|       "version": "1.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", | ||||
|       "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", | ||||
|       "license": "MIT", | ||||
|       "engines": { | ||||
|         "node": ">= 0.4" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "url": "https://github.com/sponsors/ljharb" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/has-tostringtag": { | ||||
|       "version": "1.0.2", | ||||
|       "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", | ||||
|       "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "has-symbols": "^1.0.3" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">= 0.4" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "url": "https://github.com/sponsors/ljharb" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/has-yarn": { | ||||
|       "version": "2.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", | ||||
|  | @ -2246,6 +2548,12 @@ | |||
|       "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", | ||||
|       "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==" | ||||
|     }, | ||||
|     "node_modules/lodash.merge": { | ||||
|       "version": "4.6.2", | ||||
|       "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", | ||||
|       "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", | ||||
|       "license": "MIT" | ||||
|     }, | ||||
|     "node_modules/lowercase-keys": { | ||||
|       "version": "1.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", | ||||
|  | @ -2287,6 +2595,15 @@ | |||
|         "semver": "bin/semver.js" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/math-intrinsics": { | ||||
|       "version": "1.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", | ||||
|       "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", | ||||
|       "license": "MIT", | ||||
|       "engines": { | ||||
|         "node": ">= 0.4" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/media-typer": { | ||||
|       "version": "0.3.0", | ||||
|       "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", | ||||
|  | @ -2326,19 +2643,21 @@ | |||
|       } | ||||
|     }, | ||||
|     "node_modules/mime-db": { | ||||
|       "version": "1.48.0", | ||||
|       "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz", | ||||
|       "integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==", | ||||
|       "version": "1.52.0", | ||||
|       "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", | ||||
|       "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", | ||||
|       "license": "MIT", | ||||
|       "engines": { | ||||
|         "node": ">= 0.6" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/mime-types": { | ||||
|       "version": "2.1.31", | ||||
|       "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz", | ||||
|       "integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==", | ||||
|       "version": "2.1.35", | ||||
|       "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", | ||||
|       "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "mime-db": "1.48.0" | ||||
|         "mime-db": "1.52.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">= 0.6" | ||||
|  | @ -2589,6 +2908,48 @@ | |||
|         "node": ">=10" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/node-fetch": { | ||||
|       "version": "2.7.0", | ||||
|       "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", | ||||
|       "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "whatwg-url": "^5.0.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": "4.x || >=6.0.0" | ||||
|       }, | ||||
|       "peerDependencies": { | ||||
|         "encoding": "^0.1.0" | ||||
|       }, | ||||
|       "peerDependenciesMeta": { | ||||
|         "encoding": { | ||||
|           "optional": true | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/node-fetch/node_modules/tr46": { | ||||
|       "version": "0.0.3", | ||||
|       "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", | ||||
|       "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", | ||||
|       "license": "MIT" | ||||
|     }, | ||||
|     "node_modules/node-fetch/node_modules/webidl-conversions": { | ||||
|       "version": "3.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", | ||||
|       "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", | ||||
|       "license": "BSD-2-Clause" | ||||
|     }, | ||||
|     "node_modules/node-fetch/node_modules/whatwg-url": { | ||||
|       "version": "5.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", | ||||
|       "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "tr46": "~0.0.3", | ||||
|         "webidl-conversions": "^3.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/nodemon": { | ||||
|       "version": "2.0.9", | ||||
|       "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.9.tgz", | ||||
|  | @ -2967,6 +3328,22 @@ | |||
|         "node": ">=8.10.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/redis": { | ||||
|       "version": "5.5.6", | ||||
|       "resolved": "https://registry.npmjs.org/redis/-/redis-5.5.6.tgz", | ||||
|       "integrity": "sha512-hbpqBfcuhWHOS9YLNcXcJ4akNr7HFX61Dq3JuFZ9S7uU7C7kvnzuH2PDIXOP62A3eevvACoG8UacuXP3N07xdg==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@redis/bloom": "5.5.6", | ||||
|         "@redis/client": "5.5.6", | ||||
|         "@redis/json": "5.5.6", | ||||
|         "@redis/search": "5.5.6", | ||||
|         "@redis/time-series": "5.5.6" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">= 18" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/redis-errors": { | ||||
|       "version": "1.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", | ||||
|  | @ -3738,6 +4115,20 @@ | |||
|     } | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@hubspot/api-client": { | ||||
|       "version": "13.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/@hubspot/api-client/-/api-client-13.0.0.tgz", | ||||
|       "integrity": "sha512-sXC1kRna+2uFlct1E1IQJQi53X36k24jRctmTTKzlvUM8it5VHXt6PCjVskQ4faLYGPyx9xh9GNx0GI7RMY4CQ==", | ||||
|       "requires": { | ||||
|         "@types/node": "*", | ||||
|         "@types/node-fetch": "^2.5.7", | ||||
|         "bottleneck": "^2.19.5", | ||||
|         "es6-promise": "^4.2.4", | ||||
|         "form-data": "^2.5.0", | ||||
|         "lodash.merge": "^4.6.2", | ||||
|         "node-fetch": "^2.6.0" | ||||
|       } | ||||
|     }, | ||||
|     "@ioredis/commands": { | ||||
|       "version": "1.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", | ||||
|  | @ -4091,6 +4482,38 @@ | |||
|         "@opentelemetry/instrumentation": "^0.52.0 || ^0.53.0 || ^0.54.0 || ^0.55.0 || ^0.56.0 || ^0.57.0" | ||||
|       } | ||||
|     }, | ||||
|     "@redis/bloom": { | ||||
|       "version": "5.5.6", | ||||
|       "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.5.6.tgz", | ||||
|       "integrity": "sha512-bNR3mxkwtfuCxNOzfV8B3R5zA1LiN57EH6zK4jVBIgzMzliNuReZXBFGnXvsi80/SYohajn78YdpYI+XNpqL+A==", | ||||
|       "requires": {} | ||||
|     }, | ||||
|     "@redis/client": { | ||||
|       "version": "5.5.6", | ||||
|       "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.5.6.tgz", | ||||
|       "integrity": "sha512-M3Svdwt6oSfyfQdqEr0L2HOJH2vK7GgCFx1NfAQvpWAT4+ljoT1L5S5cKT3dA9NJrxrOPDkdoTPWJnIrGCOcmw==", | ||||
|       "requires": { | ||||
|         "cluster-key-slot": "1.1.2" | ||||
|       } | ||||
|     }, | ||||
|     "@redis/json": { | ||||
|       "version": "5.5.6", | ||||
|       "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.5.6.tgz", | ||||
|       "integrity": "sha512-AIsoe3SsGQagqAmSQHaqxEinm5oCWr7zxPWL90kKaEdLJ+zw8KBznf2i9oK0WUFP5pFssSQUXqnscQKe2amfDQ==", | ||||
|       "requires": {} | ||||
|     }, | ||||
|     "@redis/search": { | ||||
|       "version": "5.5.6", | ||||
|       "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.5.6.tgz", | ||||
|       "integrity": "sha512-JSqasYqO0mVcHL7oxvbySRBBZYRYhFl3W7f0Da7BW8M/r0Z9wCiVrdjnN4/mKBpWZkoJT/iuisLUdPGhpKxBew==", | ||||
|       "requires": {} | ||||
|     }, | ||||
|     "@redis/time-series": { | ||||
|       "version": "5.5.6", | ||||
|       "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.5.6.tgz", | ||||
|       "integrity": "sha512-jkpcgq3NOI3TX7xEAJ3JgesJTxAx7k0m6lNxNsYdEM8KOl+xj7GaB/0CbLkoricZDmFSEAz7ClA1iK9XkGHf+Q==", | ||||
|       "requires": {} | ||||
|     }, | ||||
|     "@sentry-internal/node-cpu-profiler": { | ||||
|       "version": "2.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/@sentry-internal/node-cpu-profiler/-/node-cpu-profiler-2.1.0.tgz", | ||||
|  | @ -4218,6 +4641,29 @@ | |||
|       "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.1.tgz", | ||||
|       "integrity": "sha512-4tT2UrL5LBqDwoed9wZ6N3umC4Yhz3W3FloMmiiG4JwmUJWpie0c7lcnUNd4gtMKuDEO4wRVS8B6Xa0uMRsMKg==" | ||||
|     }, | ||||
|     "@types/node-fetch": { | ||||
|       "version": "2.6.12", | ||||
|       "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", | ||||
|       "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", | ||||
|       "requires": { | ||||
|         "@types/node": "*", | ||||
|         "form-data": "^4.0.0" | ||||
|       }, | ||||
|       "dependencies": { | ||||
|         "form-data": { | ||||
|           "version": "4.0.3", | ||||
|           "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", | ||||
|           "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", | ||||
|           "requires": { | ||||
|             "asynckit": "^0.4.0", | ||||
|             "combined-stream": "^1.0.8", | ||||
|             "es-set-tostringtag": "^2.1.0", | ||||
|             "hasown": "^2.0.2", | ||||
|             "mime-types": "^2.1.12" | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "@types/pg": { | ||||
|       "version": "8.6.1", | ||||
|       "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.6.1.tgz", | ||||
|  | @ -4449,6 +4895,11 @@ | |||
|         "xml2js": "^0.5.0" | ||||
|       } | ||||
|     }, | ||||
|     "bottleneck": { | ||||
|       "version": "2.19.5", | ||||
|       "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", | ||||
|       "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==" | ||||
|     }, | ||||
|     "boxen": { | ||||
|       "version": "4.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", | ||||
|  | @ -4530,6 +4981,15 @@ | |||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "call-bind-apply-helpers": { | ||||
|       "version": "1.0.2", | ||||
|       "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", | ||||
|       "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", | ||||
|       "requires": { | ||||
|         "es-errors": "^1.3.0", | ||||
|         "function-bind": "^1.1.2" | ||||
|       } | ||||
|     }, | ||||
|     "camelcase": { | ||||
|       "version": "5.3.1", | ||||
|       "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", | ||||
|  | @ -4798,6 +5258,16 @@ | |||
|       "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", | ||||
|       "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==" | ||||
|     }, | ||||
|     "dunder-proto": { | ||||
|       "version": "1.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", | ||||
|       "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", | ||||
|       "requires": { | ||||
|         "call-bind-apply-helpers": "^1.0.1", | ||||
|         "es-errors": "^1.3.0", | ||||
|         "gopd": "^1.2.0" | ||||
|       } | ||||
|     }, | ||||
|     "duplexer3": { | ||||
|       "version": "0.1.4", | ||||
|       "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", | ||||
|  | @ -4871,6 +5341,40 @@ | |||
|       "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz", | ||||
|       "integrity": "sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==" | ||||
|     }, | ||||
|     "es-define-property": { | ||||
|       "version": "1.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", | ||||
|       "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==" | ||||
|     }, | ||||
|     "es-errors": { | ||||
|       "version": "1.3.0", | ||||
|       "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", | ||||
|       "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" | ||||
|     }, | ||||
|     "es-object-atoms": { | ||||
|       "version": "1.1.1", | ||||
|       "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", | ||||
|       "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", | ||||
|       "requires": { | ||||
|         "es-errors": "^1.3.0" | ||||
|       } | ||||
|     }, | ||||
|     "es-set-tostringtag": { | ||||
|       "version": "2.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", | ||||
|       "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", | ||||
|       "requires": { | ||||
|         "es-errors": "^1.3.0", | ||||
|         "get-intrinsic": "^1.2.6", | ||||
|         "has-tostringtag": "^1.0.2", | ||||
|         "hasown": "^2.0.2" | ||||
|       } | ||||
|     }, | ||||
|     "es6-promise": { | ||||
|       "version": "4.2.8", | ||||
|       "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", | ||||
|       "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" | ||||
|     }, | ||||
|     "escape-goat": { | ||||
|       "version": "2.1.1", | ||||
|       "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", | ||||
|  | @ -5003,6 +5507,25 @@ | |||
|       "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", | ||||
|       "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==" | ||||
|     }, | ||||
|     "form-data": { | ||||
|       "version": "2.5.3", | ||||
|       "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.3.tgz", | ||||
|       "integrity": "sha512-XHIrMD0NpDrNM/Ckf7XJiBbLl57KEhT3+i3yY+eWm+cqYZJQTZrKo8Y8AWKnuV5GT4scfuUGt9LzNoIx3dU1nQ==", | ||||
|       "requires": { | ||||
|         "asynckit": "^0.4.0", | ||||
|         "combined-stream": "^1.0.8", | ||||
|         "es-set-tostringtag": "^2.1.0", | ||||
|         "mime-types": "^2.1.35", | ||||
|         "safe-buffer": "^5.2.1" | ||||
|       }, | ||||
|       "dependencies": { | ||||
|         "safe-buffer": { | ||||
|           "version": "5.2.1", | ||||
|           "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", | ||||
|           "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "forwarded": { | ||||
|       "version": "0.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", | ||||
|  | @ -5028,6 +5551,32 @@ | |||
|       "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", | ||||
|       "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" | ||||
|     }, | ||||
|     "get-intrinsic": { | ||||
|       "version": "1.3.0", | ||||
|       "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", | ||||
|       "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", | ||||
|       "requires": { | ||||
|         "call-bind-apply-helpers": "^1.0.2", | ||||
|         "es-define-property": "^1.0.1", | ||||
|         "es-errors": "^1.3.0", | ||||
|         "es-object-atoms": "^1.1.1", | ||||
|         "function-bind": "^1.1.2", | ||||
|         "get-proto": "^1.0.1", | ||||
|         "gopd": "^1.2.0", | ||||
|         "has-symbols": "^1.1.0", | ||||
|         "hasown": "^2.0.2", | ||||
|         "math-intrinsics": "^1.1.0" | ||||
|       } | ||||
|     }, | ||||
|     "get-proto": { | ||||
|       "version": "1.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", | ||||
|       "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", | ||||
|       "requires": { | ||||
|         "dunder-proto": "^1.0.1", | ||||
|         "es-object-atoms": "^1.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "get-stream": { | ||||
|       "version": "4.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", | ||||
|  | @ -5068,6 +5617,11 @@ | |||
|         "ini": "1.3.7" | ||||
|       } | ||||
|     }, | ||||
|     "gopd": { | ||||
|       "version": "1.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", | ||||
|       "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==" | ||||
|     }, | ||||
|     "got": { | ||||
|       "version": "9.6.0", | ||||
|       "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", | ||||
|  | @ -5099,6 +5653,19 @@ | |||
|       "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "has-symbols": { | ||||
|       "version": "1.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", | ||||
|       "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==" | ||||
|     }, | ||||
|     "has-tostringtag": { | ||||
|       "version": "1.0.2", | ||||
|       "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", | ||||
|       "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", | ||||
|       "requires": { | ||||
|         "has-symbols": "^1.0.3" | ||||
|       } | ||||
|     }, | ||||
|     "has-yarn": { | ||||
|       "version": "2.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", | ||||
|  | @ -5381,6 +5948,11 @@ | |||
|       "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", | ||||
|       "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==" | ||||
|     }, | ||||
|     "lodash.merge": { | ||||
|       "version": "4.6.2", | ||||
|       "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", | ||||
|       "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" | ||||
|     }, | ||||
|     "lowercase-keys": { | ||||
|       "version": "1.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", | ||||
|  | @ -5409,6 +5981,11 @@ | |||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "math-intrinsics": { | ||||
|       "version": "1.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", | ||||
|       "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==" | ||||
|     }, | ||||
|     "media-typer": { | ||||
|       "version": "0.3.0", | ||||
|       "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", | ||||
|  | @ -5436,16 +6013,16 @@ | |||
|       "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" | ||||
|     }, | ||||
|     "mime-db": { | ||||
|       "version": "1.48.0", | ||||
|       "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz", | ||||
|       "integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==" | ||||
|       "version": "1.52.0", | ||||
|       "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", | ||||
|       "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" | ||||
|     }, | ||||
|     "mime-types": { | ||||
|       "version": "2.1.31", | ||||
|       "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz", | ||||
|       "integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==", | ||||
|       "version": "2.1.35", | ||||
|       "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", | ||||
|       "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", | ||||
|       "requires": { | ||||
|         "mime-db": "1.48.0" | ||||
|         "mime-db": "1.52.0" | ||||
|       } | ||||
|     }, | ||||
|     "mimic-response": { | ||||
|  | @ -5622,6 +6199,35 @@ | |||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "node-fetch": { | ||||
|       "version": "2.7.0", | ||||
|       "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", | ||||
|       "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", | ||||
|       "requires": { | ||||
|         "whatwg-url": "^5.0.0" | ||||
|       }, | ||||
|       "dependencies": { | ||||
|         "tr46": { | ||||
|           "version": "0.0.3", | ||||
|           "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", | ||||
|           "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" | ||||
|         }, | ||||
|         "webidl-conversions": { | ||||
|           "version": "3.0.1", | ||||
|           "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", | ||||
|           "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" | ||||
|         }, | ||||
|         "whatwg-url": { | ||||
|           "version": "5.0.0", | ||||
|           "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", | ||||
|           "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", | ||||
|           "requires": { | ||||
|             "tr46": "~0.0.3", | ||||
|             "webidl-conversions": "^3.0.0" | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "nodemon": { | ||||
|       "version": "2.0.9", | ||||
|       "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.9.tgz", | ||||
|  | @ -5903,6 +6509,18 @@ | |||
|         "picomatch": "^2.2.1" | ||||
|       } | ||||
|     }, | ||||
|     "redis": { | ||||
|       "version": "5.5.6", | ||||
|       "resolved": "https://registry.npmjs.org/redis/-/redis-5.5.6.tgz", | ||||
|       "integrity": "sha512-hbpqBfcuhWHOS9YLNcXcJ4akNr7HFX61Dq3JuFZ9S7uU7C7kvnzuH2PDIXOP62A3eevvACoG8UacuXP3N07xdg==", | ||||
|       "requires": { | ||||
|         "@redis/bloom": "5.5.6", | ||||
|         "@redis/client": "5.5.6", | ||||
|         "@redis/json": "5.5.6", | ||||
|         "@redis/search": "5.5.6", | ||||
|         "@redis/time-series": "5.5.6" | ||||
|       } | ||||
|     }, | ||||
|     "redis-errors": { | ||||
|       "version": "1.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", | ||||
|  |  | |||
|  | @ -10,6 +10,7 @@ | |||
|   "author": "", | ||||
|   "license": "ISC", | ||||
|   "dependencies": { | ||||
|     "@hubspot/api-client": "^13.0.0", | ||||
|     "@sentry/node": "^9.9.0", | ||||
|     "@sentry/profiling-node": "^9.9.0", | ||||
|     "axios": "^1.6.1", | ||||
|  | @ -28,6 +29,7 @@ | |||
|     "mongoose": "^7.3.1", | ||||
|     "morgan": "^1.10.0", | ||||
|     "multer": "^1.4.5-lts.1", | ||||
|     "redis": "^5.5.6", | ||||
|     "socket.io": "^4.7.2", | ||||
|     "swagger-ui-express": "^4.1.6", | ||||
|     "xss-clean": "^0.1.1", | ||||
|  |  | |||
|  | @ -0,0 +1,70 @@ | |||
| 
 | ||||
| ## Atenção | ||||
| Para rodar a aplicação esteja na pasta backend. O .env deve estar dentro da pasta backend para rodar essa api.  | ||||
| ## Use o env.example | ||||
| 
 | ||||
| ##  Nova Funcionalidade: Integração de Transcrição com HubSpot | ||||
| 
 | ||||
| Esta funcionalidade permite receber, processar e registrar transcrições de chamadas telefônicas diretamente no HubSpot, com associação automática ao contato e, se disponível, ao ticket da chamada. | ||||
| 
 | ||||
| ### Fluxo Geral | ||||
| 
 | ||||
| 1. Uma transcrição de chamada é recebida via endpoint REST (`/api/v1/crm/transcriptions`). | ||||
| 2. O sistema consulta o Redis para verificar se há um `ticketId` associado ao `callerId`. | ||||
| 3. Se existir: | ||||
|    - A nota é associada ao contato e ao ticket correspondente no HubSpot. | ||||
| 4. Se não existir: | ||||
|    - A nota é criada e associada apenas ao contato. | ||||
| 
 | ||||
| ### Exemplo de Requisição | ||||
| 
 | ||||
| **Endpoint:** `POST /api/v1/crm/transcriptions` | ||||
| 
 | ||||
| **Body:** | ||||
| ```json | ||||
| { | ||||
|   "callerId": "5511987654321", | ||||
|   "uniqueId": "chamada-001", | ||||
|   "transcription": { | ||||
|     "summary": "Cliente solicitou informações sobre o pedido.", | ||||
|     "client": "Gostaria de saber como está meu pedido.", | ||||
|     "agent": "Claro! Posso verificar agora." | ||||
|   }, | ||||
|   "recordingUrl": "https://exemplo.com/gravação/audio.wav" | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| ### Verificação Prévia de Ticket | ||||
| 
 | ||||
| Antes de enviar a transcrição de uma chamada, o sistema realiza uma verificação para saber se já existe um ticket associado ao número de telefone (`callerId` / `crmPhone`). | ||||
| 
 | ||||
| **Endpoint:**   | ||||
| `GET /api/v1/crm/tickets/check-by-crmphone` | ||||
| 
 | ||||
| **Parâmetros de consulta (query params):** | ||||
| 
 | ||||
| | Parâmetro | Tipo   | Obrigatório | Descrição                              | | ||||
| |-----------|--------|-------------|----------------------------------------| | ||||
| | crmPhone  | string | Sim         | Número do telefone do cliente (E.164)  | | ||||
| 
 | ||||
| **Exemplo de Requisição:** | ||||
| 
 | ||||
| ``` | ||||
| GET /api/v1/crm/tickets/check-by-crmphone?crmPhone=5511997532324 | ||||
| ``` | ||||
| 
 | ||||
| **Resposta (200 OK):** | ||||
| 
 | ||||
| ```json | ||||
| { | ||||
|   "hasTicket": true, | ||||
|   "ticketId": "25292628260" | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| ### Lógica de Uso | ||||
| 
 | ||||
| - Se `hasTicket` for `true`, o `ticketId` será incluído no envio da transcrição. | ||||
| - Se `hasTicket` for `false`, a transcrição **não será enviada** para essa api para o envio ao HubSpot. | ||||
|  | @ -1,8 +1,10 @@ | |||
| const express = require('express') | ||||
| const router = express.Router() | ||||
| const { authorization, } = require('../middleware/authentication') | ||||
| const { contactCreate, sfCreateCase, sfUpdateCase, createTicket, testTemplate, webhook_crm, uploadCrmConfig, callJournaling, oauthCallBack, install, deleteCrm, deleteCompany, getCrms, webhook } = 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 { fileUpload } = require("../utils") | ||||
| const { associateTicketToCaller } = require('../controllers/crmController'); | ||||
| 
 | ||||
| router.route('/create-contact').post(authorization, contactCreate) | ||||
| router.route('/create-ticket').post(authorization, createTicket) | ||||
|  | @ -17,7 +19,8 @@ router.route('/install').get(install) | |||
| router.route('/test').post(testTemplate) | ||||
| router.route('/webhook').post(webhook) | ||||
| router.route('/webhook-crm').post(webhook_crm) | ||||
| router.route('/transcriptions').post( receiveTranscription) | ||||
| router.route('/:companyId').get(authorization, getCrms) | ||||
| 
 | ||||
| router.route('/tickets/check-by-crmphone').get(authorization, checkTicketByCrmPhone) | ||||
| 
 | ||||
| module.exports = router | ||||
|  |  | |||
|  | @ -0,0 +1,134 @@ | |||
| const axios = require('axios'); | ||||
| const Logger = console;  | ||||
| require('dotenv').config(); | ||||
| 
 | ||||
| /** | ||||
|  * Serviço para integração com a API do HubSpot. | ||||
|  */ | ||||
| class HubspotService { | ||||
|   /** | ||||
|    * Inicializa o serviço HubspotService com configuração da API. | ||||
|    */ | ||||
|   constructor() { | ||||
|     this.logger = Logger; | ||||
|     this.baseUrl = 'https://api.hubapi.com'; | ||||
|     this.apiKey = process.env.HUBSPOT_API_KEY; | ||||
|     this.client = axios.create({ | ||||
|       baseURL: this.baseUrl, | ||||
|       headers: { | ||||
|         'Authorization': `Bearer ${this.apiKey}`, | ||||
|         'Content-Type': 'application/json' | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Cria uma nota de chamada no HubSpot e associa ao contato e opcionalmente a um ticket. | ||||
|    *  | ||||
|    * @param {string} contactId - ID do contato no HubSpot. | ||||
|    * @param {Object} callData - Dados da chamada. | ||||
|    * @param {string} callData.transcription - Transcrição completa da chamada. | ||||
|    * @param {string} callData.summary - Resumo da chamada. | ||||
|    * @param {string} callData.recordingUrl - URL da gravação da chamada. | ||||
|    * @param {string} callData.callerId - Número do chamador. | ||||
|    * @param {string} callData.uniqueId - Identificador único da chamada. | ||||
|    * @param {string} [callData.ticketId] - (Opcional) ID do ticket no HubSpot. | ||||
|    * @returns {Promise<Object>} - Dados da nota criada. | ||||
|    */ | ||||
|   async createCallNote(contactId, callData) { | ||||
|     try { | ||||
|       const { transcription, summary, recordingUrl, callerId, uniqueId, ticketId } = callData; | ||||
| 
 | ||||
|       const noteContent = ` | ||||
| Chamada Recebida | ||||
| ------------------ | ||||
| Número: ${callerId} | ||||
| ID da Chamada: ${uniqueId} | ||||
| Data/Hora: ${new Date().toLocaleString('pt-BR', { timeZone: 'America/Sao_Paulo' })} | ||||
| 
 | ||||
| 
 | ||||
| Resumo: | ||||
| ${summary} | ||||
| 
 | ||||
| Transcrição Completa: | ||||
| ${transcription} | ||||
| 
 | ||||
| Link da Gravação: ${recordingUrl} | ||||
|       `.trim();
 | ||||
| 
 | ||||
|       // 1. Cria a nota
 | ||||
|       const response = await this.client.post('/crm/v3/objects/notes', { | ||||
|         properties: { | ||||
|           hs_timestamp: new Date().toISOString(), | ||||
|           hs_note_body: noteContent | ||||
|         } | ||||
|       }); | ||||
| 
 | ||||
|       const noteId = response.data.id; | ||||
| 
 | ||||
|       // 2. Associa a nota ao contato
 | ||||
|       await this.client.put(`/crm/v3/objects/notes/${noteId}/associations/contact/${contactId}/note_to_contact`); | ||||
| 
 | ||||
|       // 3. (Opcional) Associa a nota ao ticket, se houver
 | ||||
|       if (ticketId) { | ||||
|         await this.client.put(`/crm/v3/objects/notes/${noteId}/associations/ticket/${ticketId}/note_to_ticket`); | ||||
|         this.logger.log(`Nota associada ao ticket ${ticketId}`); | ||||
|       } | ||||
| 
 | ||||
|       this.logger.log(`Nota ${noteId} criada com sucesso.`); | ||||
|       return response.data; | ||||
| 
 | ||||
|     } catch (error) { | ||||
|       this.logger.error('Erro ao criar nota:', error?.response?.data || error.message); | ||||
|       throw error; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Busca um contato no HubSpot pelo número de telefone. | ||||
|    *  | ||||
|    * @param {string} phoneNumber - Número de telefone para buscar. | ||||
|    * @returns {Promise<Object|null>} - Contato encontrado ou null. | ||||
|    */ | ||||
|   async findContactByPhone(phoneNumber) { | ||||
|     try { | ||||
|       const response = await this.client.post('/crm/v3/objects/contacts/search', { | ||||
|         filterGroups: [{ | ||||
|           filters: [{ | ||||
|             propertyName: 'phone', | ||||
|             operator: 'EQ', | ||||
|             value: phoneNumber | ||||
|           }] | ||||
|         }] | ||||
|       }); | ||||
| 
 | ||||
|       return response.data.results[0] || null; | ||||
|     } catch (error) { | ||||
|       this.logger.error('Erro ao buscar contato:', error?.response?.data || error.message); | ||||
|       throw error; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Cria um contato com o número informado caso não exista. | ||||
|    *  | ||||
|    * @param {string} phoneNumber - Número de telefone do contato. | ||||
|    * @returns {Promise<Object>} - Contato existente ou novo contato criado. | ||||
|    */ | ||||
|   async createContactIfNotExists(phoneNumber) { | ||||
|     const existing = await this.findContactByPhone(phoneNumber); | ||||
|     if (existing) return existing; | ||||
| 
 | ||||
|     const response = await this.client.post('/crm/v3/objects/contacts', { | ||||
|       properties: { | ||||
|         phone: phoneNumber, | ||||
|          | ||||
|       } | ||||
|     }); | ||||
| 
 | ||||
|     this.logger.log(`Contato criado para o número ${phoneNumber}`); | ||||
|     return response.data; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| module.exports = HubspotService; | ||||
|  | @ -2,9 +2,14 @@ const Redis = require("ioredis") | |||
| const redis = new Redis(process.env.REDIS_URI) | ||||
| 
 | ||||
| // Function to set a token with expiration
 | ||||
| async function set(key, value, expirationInSeconds) { | ||||
|     await redis.set(key, value, 'EX', expirationInSeconds) | ||||
|     console.log(`Token ${key} set successfully with expiration of ${expirationInSeconds} seconds!`) | ||||
| async function set(key, value, expirationInSeconds = null) { | ||||
|     if (expirationInSeconds) { | ||||
|         await redis.set(key, value, 'EX', expirationInSeconds) | ||||
|         console.log(`Token ${key} set successfully with expiration of ${expirationInSeconds} seconds!`) | ||||
|     } else { | ||||
|         await redis.set(key, value) | ||||
|         console.log(`Token ${key} set successfully without expiration!`) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // Function to get a token
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue