fix: adjust API key handling and optimize call journaling

feat/hitphone-socket-integration
adriano 2024-07-18 21:41:03 -03:00
parent 050d711abc
commit 8f9afd5f23
13 changed files with 439 additions and 31 deletions

View File

@ -0,0 +1,188 @@
{
"authentication":{
"type": "bearer",
"token": "pat-na1-7aca13dd-9ba5-48db-bf35-570844d31abb",
"crmPhoneTest": "5511988334455"
},
"crmRest":[
{
"createContactRecord":{
"request":{
"requestContentType":"application/json",
"requestEncoding":"Json",
"requestType":"Post",
"responseType":"Json",
"url":"https://api.hubapi.com/contacts/v1/contact"
},
"body":{
"properties":[
{
"property":"phone",
"value":"crmPhone"
}
]
},
"response":{
"id":"vid"
}
}
},
{
"lookupContactByPhone":{
"request":{
"requestContentType":"application/json",
"requestEncoding":"Json",
"requestType":"Get",
"responseType":"Json",
"url":"https://api.hubapi.com/contacts/v1/search/query?q=crmPhone"
},
"response":{
"phone":"contacts.properties.phone.value",
"id":"contacts.vid"
}
}
},
{
"callJournaling":{
"request":{
"requestContentType":"application/json",
"requestEncoding":"Json",
"requestType":"Post",
"responseType":"Json",
"url":"https://api.hubapi.com/engagements/v1/engagements"
},
"calls": [
{
"inboundAnsweredCall": {
"engagement": {
"active": true,
"type": "CALL"
},
"associations": {
"contactIds": [
{
"_prop": "crmContactId",
"_type": "number"
}
]
},
"metadata": {
"toNumber": {
"_prop": "crmAgent",
"_type": "string"
},
"fromNumber": {
"_prop": "crmPhone",
"_type": "string"
},
"status": "COMPLETED",
"durationMilliseconds": {
"_prop": "crmCallDuration",
"_type": "number",
"_format": "milliseconds"
},
"body": "Ligação recebida - inbound call",
"disposition": "f240bbac-87c9-4f6e-bf70-924b57d47db7"
}
}
},
{
"inboundMissedCall": {
"engagement": {
"active": true,
"type": "CALL"
},
"associations": {
"contactIds": [
{
"_prop": "crmContactId",
"_type": "number"
}
]
},
"metadata": {
"toNumber": {
"_prop": "crmAgent",
"_type": "string"
},
"fromNumber": {
"_prop": "crmPhone",
"_type": "string"
},
"status": "COMPLETED",
"body": "Ligação perdida - inbound call",
"disposition": "f240bbac-87c9-4f6e-bf70-924b57d47db7"
}
}
},
{
"outboundAnsweredCall": {
"engagement": {
"active": true,
"type": "CALL"
},
"associations": {
"contactIds": [
{
"_prop": "crmContactId",
"_type": "number"
}
]
},
"metadata": {
"fromNumber": {
"_prop": "crmPhone",
"_type": "string"
},
"toNumber": {
"_prop": "crmAgent",
"_type": "string"
},
"status": "COMPLETED",
"durationMilliseconds": {
"_prop": "crmCallDuration",
"_type": "number",
"_format": "milliseconds"
},
"body": "Ligação atendida - outbound call",
"disposition": "f240bbac-87c9-4f6e-bf70-924b57d47db7"
}
}
},
{
"outboundUnansweredCall": {
"engagement": {
"active": true,
"type": "CALL"
},
"associations": {
"contactIds": [
{
"_prop": "crmContactId",
"_type": "number"
}
]
},
"metadata": {
"fromNumber": {
"_prop": "crmAgent",
"_type": "string"
},
"toNumber": {
"_prop": "crmPhone",
"_type": "string"
},
"status": "COMPLETED",
"body": "Ligação perdida - oubound call",
"disposition": "f240bbac-87c9-4f6e-bf70-924b57d47db7"
}
}
}
],
"response":{
}
}
}
]
}

View File

@ -0,0 +1,188 @@
{
"authentication":{
"type": "bearer",
"token": "pat-na1-7aca13dd-9ba5-48db-bf35-570844d31abb",
"crmPhoneTest": "5511988334455"
},
"crmRest":[
{
"createContactRecord":{
"request":{
"requestContentType":"application/json",
"requestEncoding":"Json",
"requestType":"Post",
"responseType":"Json",
"url":"https://api.hubapi.com/contacts/v1/contact"
},
"body":{
"properties":[
{
"property":"phone",
"value":"crmPhone"
}
]
},
"response":{
"id":"vid"
}
}
},
{
"lookupContactByPhone":{
"request":{
"requestContentType":"application/json",
"requestEncoding":"Json",
"requestType":"Get",
"responseType":"Json",
"url":"https://api.hubapi.com/contacts/v1/search/query?q=crmPhone"
},
"response":{
"phone":"contacts.properties.phone.value",
"id":"contacts.vid"
}
}
},
{
"callJournaling":{
"request":{
"requestContentType":"application/json",
"requestEncoding":"Json",
"requestType":"Post",
"responseType":"Json",
"url":"https://api.hubapi.com/engagements/v1/engagements"
},
"calls": [
{
"inboundAnsweredCall": {
"engagement": {
"active": true,
"type": "CALL"
},
"associations": {
"contactIds": [
{
"_prop": "crmContactId",
"_type": "number"
}
]
},
"metadata": {
"toNumber": {
"_prop": "crmAgent",
"_type": "string"
},
"fromNumber": {
"_prop": "crmPhone",
"_type": "string"
},
"status": "COMPLETED",
"durationMilliseconds": {
"_prop": "crmCallDuration",
"_type": "number",
"_format": "milliseconds"
},
"body": "Ligação recebida - inbound call",
"disposition": "f240bbac-87c9-4f6e-bf70-924b57d47db7"
}
}
},
{
"inboundMissedCall": {
"engagement": {
"active": true,
"type": "CALL"
},
"associations": {
"contactIds": [
{
"_prop": "crmContactId",
"_type": "number"
}
]
},
"metadata": {
"toNumber": {
"_prop": "crmAgent",
"_type": "string"
},
"fromNumber": {
"_prop": "crmPhone",
"_type": "string"
},
"status": "COMPLETED",
"body": "Ligação perdida - inbound call",
"disposition": "f240bbac-87c9-4f6e-bf70-924b57d47db7"
}
}
},
{
"outboundAnsweredCall": {
"engagement": {
"active": true,
"type": "CALL"
},
"associations": {
"contactIds": [
{
"_prop": "crmContactId",
"_type": "number"
}
]
},
"metadata": {
"fromNumber": {
"_prop": "crmPhone",
"_type": "string"
},
"toNumber": {
"_prop": "crmAgent",
"_type": "string"
},
"status": "COMPLETED",
"durationMilliseconds": {
"_prop": "crmCallDuration",
"_type": "number",
"_format": "milliseconds"
},
"body": "Ligação atendida - outbound call",
"disposition": "f240bbac-87c9-4f6e-bf70-924b57d47db7"
}
}
},
{
"outboundUnansweredCall": {
"engagement": {
"active": true,
"type": "CALL"
},
"associations": {
"contactIds": [
{
"_prop": "crmContactId",
"_type": "number"
}
]
},
"metadata": {
"fromNumber": {
"_prop": "crmAgent",
"_type": "string"
},
"toNumber": {
"_prop": "crmPhone",
"_type": "string"
},
"status": "COMPLETED",
"body": "Ligação perdida - oubound call",
"disposition": "f240bbac-87c9-4f6e-bf70-924b57d47db7"
}
}
}
],
"response":{
}
}
}
]
}

View File

@ -2,7 +2,7 @@
"authentication": {
"type": "api_token",
"token": "1c97c52596abc18d3f727df3a620b5ef3f4f7d29",
"crmPhoneTest": "5511988334455"
"crmPhoneTest": "5511988325936"
},
"crmRest": [
{
@ -40,8 +40,8 @@
"url": "https://api.pipedrive.com/v1/persons/search?term=crmPhone"
},
"response": {
"phone": "data.items.item.phones[0]",
"id": "data.item.id"
"phone": "data.items[0].item.phones[0]",
"id": "data.items[0].item.id"
}
}
},
@ -52,7 +52,7 @@
"requestEncoding": "Json",
"requestType": "Post",
"responseType": "Json",
"url": "https://api.hubapi.com/engagements/v1/engagements"
"url": "https://api.pipedrive.com/v1/activities"
},
"calls": [
{
@ -61,8 +61,8 @@
"type": "Call",
"person_id": "crmContactId",
"done": "1",
"deal_id": "2",
"note": "Ligação recebida +crmPhone"
"due_date": "YYYY-MM-DD",
"due_time": "HH:MM"
}
},
{
@ -71,8 +71,8 @@
"type": "Call",
"person_id": "crmContactId",
"done": "0",
"deal_id": "2",
"note": "Ligação perdida +crmPhone"
"due_date": "YYYY-MM-DD",
"due_time": "HH:MM"
}
},
{
@ -81,8 +81,9 @@
"type": "Call",
"person_id": "crmContactId",
"done": "1",
"deal_id": "2",
"note": "Ligação realizada para +crmPhone"
"due_date": "YYYY-MM-DD",
"due_time": "HH:MM",
"note": ""
}
},
{
@ -91,8 +92,8 @@
"type": "Call",
"person_id": "crmContactId",
"done": "0",
"deal_id": "2",
"note": "Ligação realizada para +crmPhone"
"due_date": "YYYY-MM-DD",
"due_time": "HH:MM"
}
}
],

View File

@ -0,0 +1 @@
pat-na1-37da6668-e0b1-44cb-bd2d-596f5f65634a

View File

@ -60,20 +60,20 @@ const deleteCompany = async (req, res) => {
const callJournaling = async (req, res) => {
const { companyId, operation, crmPhone, crmAgent, crmCallDuration } = req.body
const { companyId, operation, crmPhone, crmAgent, crmCallDuration, crmFirstName } = req.body
mustContainProperties(req, ['companyId', 'operation', 'crmPhone', 'crmAgent',])
if (operation == 'inboundAnsweredCall' && !crmCallDuration)
throw new CustomError.BadRequestError(`The crmCallDuration property must be provided when operation is inboundAnsweredCall`)
if (operation == 'outboundAsweredCall' && !crmCallDuration)
throw new CustomError.BadRequestError(`The crmCallDuration property must be provided when operation is outboundAsweredCall`)
// if (operation == 'inboundAnsweredCall' && !crmCallDuration)
// throw new CustomError.BadRequestError(`The crmCallDuration property must be provided when operation is inboundAnsweredCall`)
// if (operation == 'outboundAsweredCall' && !crmCallDuration)
// throw new CustomError.BadRequestError(`The crmCallDuration property must be provided when operation is outboundAsweredCall`)
await journaling(companyId, operation, crmPhone, crmAgent, crmCallDuration,)
await journaling(companyId, operation, crmPhone, crmAgent, crmCallDuration, crmFirstName)
res.status(StatusCodes.OK).send()
}
}
const install = async (req, res) => {
const { authUrl, companyId } = req.query
@ -224,7 +224,7 @@ module.exports = {
install,
deleteCrm,
deleteCompany,
testTemplate
testTemplate
}

View File

@ -69,7 +69,7 @@ const CRMRestSchema = new Schema({
const AuthenticationSchema = new Schema({
type: {
type: String,
enum: ['basic', 'bearer', 'oauth2'],
enum: ['basic', 'bearer', 'oauth2', "api_token"],
default: 'bearer'
},
userName: String,

View File

@ -11,7 +11,7 @@ async function createCRMContact(companyId, crmFirstName, crmPhone, crmEmail = ''
const { crmRest: rest, authentication } = crmConfig.crm
const contact = await lookupContactByPhone(rest, authentication, crmPhone, companyId)
if (contact.exist) continue
await createContact(companyId, rest, authentication, crmPhone, crmFirstName, crmLastName, crmEmail,)

View File

@ -8,9 +8,9 @@ const CRM = require('../models/CRM')
const requestConfigHeader = require('./requestConfigHeader')
const sendMessageSocket = require('./sendMessageSocket')
async function createContact(companyId, rest, authentication, crmPhone, crmFirstName = '', crmLastName = '', crmEmail = '', test = {}) {
async function createContact(companyId, rest, authentication, crmPhone, crmFirstName = 'unnamed', crmLastName = 'no surname', crmEmail = '', test = {}) {
let { request, body, response } = findProperty(rest, 'createContactRecord')
const { requestContentType, requestEncoding, requestType, responseType, url } = request
body = flatten(body)
@ -43,14 +43,14 @@ async function createContact(companyId, rest, authentication, crmPhone, crmFirst
//url, crmPhone, requestType, requestContentType, type, userName, passWord, token, crmClientId, data = ''
const config = await requestConfigHeader(url, crmPhone, requestType, requestContentType, type, userName, passWord, token, crmClientId, body)
if (test?.testing) {
msg = `Tentanto criar contato de numero ${crmPhone} no crm`
sendMessageSocket({ companyId, status: 'processing', data: { request: config, msg } })
}
let { data } = await axios(config)
data = flatten(data)
let auxContactId
@ -64,6 +64,7 @@ async function createContact(companyId, rest, authentication, crmPhone, crmFirst
break
}
}
if (auxContactId && !test?.testing) {
const crm = await CRM.findOne({ companyId, crmBaseURL: new URL(url).hostname })

View File

@ -5,7 +5,7 @@ const findProperty = require('./findProperty')
const journalingRequest = require('./journalingRequest')
async function journaling(companyId, operation, crmPhone, crmAgent, crmCallDuration = 0) {
async function journaling(companyId, operation, crmPhone, crmAgent, crmCallDuration = 0, crmFirstName ='unnamed') {
const crmFiles = await loadCRM(companyId)
@ -16,7 +16,7 @@ async function journaling(companyId, operation, crmPhone, crmAgent, crmCallDurat
let contact = await lookupContactByPhone(rest, authentication, crmPhone, companyId)
if (!contact.exist) {
contact = await createContact(companyId, rest, authentication, crmPhone)
contact = await createContact(companyId, rest, authentication, crmPhone, crmFirstName)
}
let { request, calls, response } = findProperty(rest, 'callJournaling')

View File

@ -53,6 +53,24 @@ async function lookupContactByPhone(rest, authentication, crmPhone, companyId, t
}
if (!auxPhone && !auxContactId) {
for (const prop in data) {
let _prop = prop.replace(/\.(\d+)(\.|$)/g, '[$1]$2')
if (_prop == response?.phone?.trim()) {
auxPhone = data[prop].replace('+', '')
}
if (_prop == response?.id?.trim()) {
auxContactId = data[prop]
}
if (auxPhone && auxContactId) break
}
}
if (auxPhone) {
if (auxPhone && auxContactId) {

View File

@ -4,6 +4,16 @@ async function requestConfigHeader(url, crmPhone, requestType, requestContentTyp
let config = {}
url = url.replace('crmPhone', crmPhone)
if (type == 'api_token') {
if (requestType.trim().toLowerCase() == 'post') {
url = `${url}?api_token=${token}`
}
else if (requestType.trim().toLowerCase() == 'get') {
url = `${url}&api_token=${token}`
}
}
let commonConfig = {
method: requestType,
url,
@ -47,7 +57,7 @@ async function requestConfigHeader(url, crmPhone, requestType, requestContentTyp
config = {
...commonConfig,
headers: {
...commonConfig.headers,
...commonConfig.headers,
}
}
}

View File

@ -23,7 +23,7 @@ async function templateValidator(crmPhoneTest, crm, companyId) {
rest = crm.crmRest,
authentication = crm.authentication,
crmPhone = phoneTest,
crmFirstName = '',
crmFirstName = 'unnamed',
crmLastName = '',
crmEmail = '',
test = { testing: true })

View File

@ -63,6 +63,7 @@ const UploadFile = () => {
setUploadStatus('Error uploading file.')
setStatus('error')
console.log('error: ', error)
setErrorResponse(error.response.data.msg)
}
}