2025-06-12 20:30:46 +00:00
|
|
|
const axios = require('axios');
|
2025-06-16 17:56:12 +00:00
|
|
|
const Logger = console;
|
2025-06-12 20:30:46 +00:00
|
|
|
require('dotenv').config();
|
2025-06-16 17:56:12 +00:00
|
|
|
const loadCRM = require('../utils/loadCRM')
|
|
|
|
const lookupContactByPhone = require('../utils/lookupCRMContactByPhone')
|
|
|
|
const createContact = require('../utils/createContact')
|
2025-06-12 20:30:46 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Serviço para integração com a API do HubSpot.
|
|
|
|
*/
|
|
|
|
class HubspotService {
|
|
|
|
/**
|
|
|
|
* Inicializa o serviço HubspotService com configuração da API.
|
|
|
|
*/
|
2025-06-20 13:48:48 +00:00
|
|
|
constructor(companyId) {
|
2025-06-12 20:30:46 +00:00
|
|
|
this.logger = Logger;
|
2025-06-20 13:48:48 +00:00
|
|
|
this.companyId = companyId
|
|
|
|
}
|
|
|
|
|
|
|
|
async init() {
|
|
|
|
this.crmFiles = await loadCRM(this.companyId);
|
|
|
|
|
|
|
|
if (this?.crmFiles?.length > 0) {
|
|
|
|
|
|
|
|
const { authentication } = this.crmFiles[0].crm
|
|
|
|
|
|
|
|
this.client = axios.create({
|
|
|
|
baseURL: 'https://api.hubapi.com',
|
|
|
|
headers: {
|
|
|
|
'Authorization': `Bearer ${authentication.token}`,
|
|
|
|
'Content-Type': 'application/json'
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return this;
|
2025-06-12 20:30:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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 {
|
2025-06-20 11:57:01 +00:00
|
|
|
const { transcription, summary, recordingUrl, crmPhone, uniqueId, ticketId } = callData;
|
2025-06-12 20:30:46 +00:00
|
|
|
|
|
|
|
const noteContent = `
|
|
|
|
Chamada Recebida
|
|
|
|
------------------
|
2025-06-20 11:57:01 +00:00
|
|
|
Número: ${crmPhone}
|
2025-06-12 20:30:46 +00:00
|
|
|
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.
|
|
|
|
*
|
2025-06-20 11:57:01 +00:00
|
|
|
* @param {string} phoneNumber
|
|
|
|
* @returns {Promise<Object|null>}
|
2025-06-12 20:30:46 +00:00
|
|
|
*/
|
|
|
|
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.
|
|
|
|
*
|
2025-06-16 17:56:12 +00:00
|
|
|
* @param {string} crmPhone - Número de telefone do contato.
|
2025-06-12 20:30:46 +00:00
|
|
|
* @returns {Promise<Object>} - Contato existente ou novo contato criado.
|
|
|
|
*/
|
2025-06-20 13:48:48 +00:00
|
|
|
async createContactIfNotExists(crmPhone) {
|
2025-06-20 21:06:30 +00:00
|
|
|
if (!this.crmFiles?.length) {
|
|
|
|
throw new Error(`Nenhuma configuração CRM encontrada para companyId: ${this.companyId}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
const { crmRest: rest, authentication } = this.crmFiles[0].crm;
|
|
|
|
|
|
|
|
let contact = await lookupContactByPhone(rest, authentication, crmPhone, this.companyId);
|
|
|
|
|
|
|
|
if (!contact?.exist) {
|
|
|
|
contact = await createContact(this.companyId, rest, authentication, crmPhone);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!contact || !contact.contactId) {
|
|
|
|
throw new Error(`Falha ao obter contactId para ${crmPhone}`);
|
2025-06-16 17:56:12 +00:00
|
|
|
}
|
2025-06-20 21:06:30 +00:00
|
|
|
|
|
|
|
console.log('========> contact', contact);
|
|
|
|
return contact;
|
2025-06-12 20:30:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = HubspotService;
|