From b654ce11a9ace7898de077df24946677b055465c Mon Sep 17 00:00:00 2001 From: adriano Date: Mon, 7 Oct 2024 11:25:08 -0300 Subject: [PATCH] feat: added feature to register whatsapp journaling from omnihit --- ...orce_oauth2_lead_editing_redirectlink.json | 25 +++- backend/controllers/crmController.js | 135 ++++++------------ backend/controllers/test.js | 11 ++ backend/data/omihitV2IntegrationCRM.json | 19 +++ backend/models/CRM.js | 22 +-- backend/utils/ScheduleTicketCRM.js | 69 +++++++++ backend/utils/ticketCRM.js | 21 +++ backend/utils/toTimestamp.js | 12 ++ 8 files changed, 211 insertions(+), 103 deletions(-) create mode 100644 backend/controllers/test.js create mode 100644 backend/data/omihitV2IntegrationCRM.json create mode 100644 backend/utils/ScheduleTicketCRM.js create mode 100644 backend/utils/toTimestamp.js diff --git a/backend/Templates-test/salesforce_oauth2_lead_editing_redirectlink.json b/backend/Templates-test/salesforce_oauth2_lead_editing_redirectlink.json index d3d9975..8ca1d87 100644 --- a/backend/Templates-test/salesforce_oauth2_lead_editing_redirectlink.json +++ b/backend/Templates-test/salesforce_oauth2_lead_editing_redirectlink.json @@ -151,7 +151,30 @@ ], "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.", + "ActivityDate": "YYYY-MM-DD", + "Status": "Completed", + "Priority": "Normal" + } + } + ] + } + }, { "redirectLink": { "request": { diff --git a/backend/controllers/crmController.js b/backend/controllers/crmController.js index 409133f..f0d677c 100644 --- a/backend/controllers/crmController.js +++ b/backend/controllers/crmController.js @@ -1,5 +1,6 @@ const path = require('path') +const omnihitV2Integration = require('../data/omihitV2IntegrationCRM.json') const { StatusCodes } = require("http-status-codes") const { createCRMContact, sendMessageSocket, @@ -21,6 +22,8 @@ const CustomError = require('../errors') const CRM_Contact = require('../models/CRM_Contact') const axios = require('axios') const { get, set } = require('../utils/redisClient') +const { getContactIdChatwoot, createConversation, toggleConversationStatus } = require('../utils/ScheduleTicketCRM') +const timeStamp = require('../utils/toTimestamp') const contactCreate = async (req, res) => { @@ -292,6 +295,9 @@ const getCrms = async (req, res) => { const webhook = async (req, res) => { + const originIP = req.headers['x-forwarded-for'] || req.connection.remoteAddress + console.log('========> Origem da requisição IP: ', originIP) + console.log('========> webhook req.body: ', JSON.stringify(req.body, null, 6)) console.log('========> req.body: ', req.body) @@ -329,6 +335,9 @@ const webhook = async (req, res) => { const webhook_crm = async (req, res) => { + const originIP = req.headers['x-forwarded-for'] || req.connection.remoteAddress + console.log('========> Crm Origem da requisição IP: ', originIP) + console.log('========> webhook crm req.body: ', JSON.stringify(req.body, null, 6)) const responseXml = ` @@ -347,22 +356,29 @@ const webhook_crm = async (req, res) => { return res.set('Content-Type', 'text/xml').status(StatusCodes.OK).send(responseXml) } + const url = req.body['soapenv:Envelope']['soapenv:Body']?.notifications?.EnterpriseUrl + + console.log('========> salesforce url: ', url) + + const crm = await CRM.findOne({ crmBaseURL: new URL(url.trim()).hostname }) + + const { companyId } = crm + + console.log('========> crm companyId: ', companyId) + const whoId = sObject['sf:WhoId'] const EventSubtype = sObject['sf:EventSubtype'] const StartDateTime = sObject['sf:StartDateTime'] - console.log('==========> crm EventSubtype: ', EventSubtype) console.log('==========> crm StartDateTime: ', StartDateTime) console.log('==========> crm StartDateTime timeStamp: ', timeStamp(StartDateTime)) - - if (EventSubtype == 'Event') { const contact = await CRM_Contact.findOne({ - companyId: "99", - crmBaseURL: 'nocompany-a9-dev-ed.develop.my.salesforce.com', + companyId, + crmBaseURL: new URL(url.trim()).hostname, contactId: whoId }) @@ -371,20 +387,32 @@ const webhook_crm = async (req, res) => { const { phone } = contact - const contactIdChatwoot = await getContactIdChatwoot(phone) + const obj = omnihitV2Integration.find(o => o.companyId == companyId) - if (!contactIdChatwoot) { - return res.set('Content-Type', 'text/xml').status(StatusCodes.OK).send(responseXml) - } + if (obj) { - console.log('==========> crm contactIdChatwoot: ', contactIdChatwoot) + const { companyId, + omnihit: { accountId, api: { url, token } = {}, + createConversation: { inbox_id, status, team_id } = {} } = {} } = obj - const ticketId = await createConversation(contactIdChatwoot) + const contactIdChatwoot = await getContactIdChatwoot(url, token, phone) - console.log('==========> crm ticketId: ', ticketId) + if (!contactIdChatwoot) { + return res.set('Content-Type', 'text/xml').status(StatusCodes.OK).send(responseXml) + } - await toggleConversationStatus({ "status": "snoozed", "snoozed_until": timeStamp(StartDateTime) }, ticketId) + console.log('==========> crm contactIdChatwoot: ', contactIdChatwoot) + let data = { inbox_id, contact_id: contactIdChatwoot, status, team_id } + const omnihitConfig = { url, accountId, token } + const ticketId = await createConversation(data, omnihitConfig) + + console.log('==========> crm ticketId: ', ticketId) + + data = { "status": "snoozed", "snoozed_until": timeStamp(StartDateTime) } + + await toggleConversationStatus(url, accountId, token, ticketId, data) + } } return res.set('Content-Type', 'text/xml').status(StatusCodes.OK).send(responseXml) @@ -402,86 +430,7 @@ module.exports = { getCrms, webhook, webhook_crm -} - - - - -async function getContactIdChatwoot(phone) { - - const config = { - method: 'get', - url: `http://172.31.187.47:3333/api/v1/accounts/15/contacts/search?q=${phone}`, - headers: { - 'api_access_token': 'WpeGuicvuQ3pyLvYQ11eAxxL' - } - } - - try { - const { data } = await axios(config) - - return data.payload[0].id - } catch (error) { - console.error(error) - } -} - -async function createConversation(contact_id) { - const data = JSON.stringify({ - "inbox_id": "2", - "contact_id": `${contact_id}`, - "status": "pending", - "team_id": "1" - }) - - const config = { - method: 'post', - url: 'http://172.31.187.47:3333/api/v1/accounts/15/conversations', - headers: { - 'api_access_token': 'WpeGuicvuQ3pyLvYQ11eAxxL', - 'Content-Type': 'application/json' - }, - data: data - } - - - try { - const { data } = await axios(config) - return data.id - } catch (error) { - console.error(error) - } -} - -async function toggleConversationStatus(payload, ticketId) { - - const config = { - method: 'post', - url: `http://172.31.187.47:3333/api/v1/accounts/15/conversations/${ticketId}/toggle_status`, - headers: { - 'api_access_token': 'WpeGuicvuQ3pyLvYQ11eAxxL', - 'Content-Type': 'application/json' - }, - data: JSON.stringify(payload) - } - - try { - const response = await axios(config) - console.log(JSON.stringify(response.data)) - } catch (error) { - console.error(error) - } -} - -function timeStamp(dateTimeISO8601) { - const date = new Date(dateTimeISO8601) - - date.setHours(date.getUTCHours() - 3) - - const timestamp = date.getTime() / 1000 - - return timestamp -} +} diff --git a/backend/controllers/test.js b/backend/controllers/test.js new file mode 100644 index 0000000..8402885 --- /dev/null +++ b/backend/controllers/test.js @@ -0,0 +1,11 @@ +const omnihitV2Integration = require('../data/omihitV2IntegrationCRM.json') + +const obj = omnihitV2Integration.find(o => o.companyId === "99") + +if (obj) { + console.log(obj) + + const { companyId, omnihit: { accountId, api: { url, token } = {}, createConversation: { inbox_id, status, team_id } = {} } = {} } = obj + + console.log('companyId: ', companyId, ' accountId: ', accountId, ' url: ', url, ' token: ', token, ' inbox_id: ', inbox_id, ' status: ', status, ' team_id: ', team_id) +} diff --git a/backend/data/omihitV2IntegrationCRM.json b/backend/data/omihitV2IntegrationCRM.json new file mode 100644 index 0000000..9ca910f --- /dev/null +++ b/backend/data/omihitV2IntegrationCRM.json @@ -0,0 +1,19 @@ +[ + { + "companyId":"99", + "omnihit": { + "accountId":"15", + "api":{ + "url":"http://172.31.187.47:3333", + "token": "WpeGuicvuQ3pyLvYQ11eAxxL" + }, + "createConversation":{ + "inbox_id": "2", + "status":"pending", + "team_id": "1" + } + + } + } + +] \ No newline at end of file diff --git a/backend/models/CRM.js b/backend/models/CRM.js index 3f01141..969f52c 100644 --- a/backend/models/CRM.js +++ b/backend/models/CRM.js @@ -38,13 +38,22 @@ const CallsSchema = new Schema({ outboundUnansweredCall: Object }) +const ChatsSchema = new Schema({ + chatDone: Object, +}) + const CallJournalingSchema = new Schema({ request: RequestSchema, calls: [CallsSchema], response: Object }) - +const ChatJournalingSchema = new Schema({ + request: RequestSchema, + chats: [ChatsSchema], + response: Object +}) + const RedirectUrlSchema = new Schema({ url: { @@ -52,13 +61,7 @@ const RedirectUrlSchema = new Schema({ required: false } }) - -// const redirectLinkSchema = new Schema({ -// request: RedirectUrlSchema, -// }) - - - + // Define main schema const CRMRestSchema = new Schema({ @@ -91,7 +94,8 @@ const CRMRestSchema = new Schema({ redirectLink: { request: RedirectUrlSchema }, - callJournaling: CallJournalingSchema + callJournaling: CallJournalingSchema, + chatJournaling: ChatJournalingSchema }) const AuthenticationSchema = new Schema({ diff --git a/backend/utils/ScheduleTicketCRM.js b/backend/utils/ScheduleTicketCRM.js new file mode 100644 index 0000000..25c8e9b --- /dev/null +++ b/backend/utils/ScheduleTicketCRM.js @@ -0,0 +1,69 @@ +const axios = require('axios') + +async function getContactIdChatwoot(url, token, phone) { + + const config = { + method: 'get', + url: `${url}/api/v1/accounts/15/contacts/search?q=${phone}`, + headers: { + 'api_access_token': token + } + } + + try { + const { data } = await axios(config) + + return data.payload[0].id + } catch (error) { + console.error(error) + } +} + +async function createConversation(data, omnihitConfig) { + + const { url, accountId, token } = omnihitConfig + + const config = { + method: 'post', + url: `${url}/api/v1/accounts/${accountId}/conversations`, + headers: { + 'api_access_token': token, + 'Content-Type': 'application/json' + }, + data: data + } + + + try { + const { data } = await axios(config) + return data.id + } catch (error) { + console.error(error) + } +} + +async function toggleConversationStatus(url, accountId, token, ticketId, payload) { + + const config = { + method: 'post', + url: `${url}/api/v1/accounts/${accountId}/conversations/${ticketId}/toggle_status`, + headers: { + 'api_access_token': token, + 'Content-Type': 'application/json' + }, + data: JSON.stringify(payload) + } + + try { + const response = await axios(config) + console.log(JSON.stringify(response.data)) + } catch (error) { + console.error(error) + } +} + +module.exports = { + getContactIdChatwoot, + createConversation, + toggleConversationStatus +} diff --git a/backend/utils/ticketCRM.js b/backend/utils/ticketCRM.js index 43eef3e..57a90be 100644 --- a/backend/utils/ticketCRM.js +++ b/backend/utils/ticketCRM.js @@ -9,6 +9,7 @@ const createTicket = require('./createTicket') const lookupCRMTicket = require('./lookupCRMTicket') const sendEventTicketCreatedToSocket = require('./sendEventTicketCreatedToSocket') +const journalingRequest = require('./journalingRequest') async function ticketCRM(companyId, crmPhone, crmAgent, crmFirstName = 'Username') { @@ -21,6 +22,26 @@ async function ticketCRM(companyId, crmPhone, crmAgent, crmFirstName = 'Username const { crmRest: rest, authentication } = crmConfig.crm + // Record whatsapp conversation that happened in omnihit v2 + let chatJournaling = findProperty(rest, 'chatJournaling') + + console.log('===============> chatJournaling: ', chatJournaling) + + + if (chatJournaling) { + + let contact = await _lookupContact(rest, authentication, crmPhone, companyId, crmFirstName) + + const { contactId, created } = contact + + let { request, chats, response } = chatJournaling + + let body = findProperty(chats, 'chatDone') + + await journalingRequest(request, body, crmCallDuration = 0, contact, crmAgent, crmPhone, authentication, rest) + } + + // Send the edited contact/lead link url to hitphone to open on another browser tab let redirectLink = findProperty(rest, 'redirectLink') diff --git a/backend/utils/toTimestamp.js b/backend/utils/toTimestamp.js new file mode 100644 index 0000000..4dab6fb --- /dev/null +++ b/backend/utils/toTimestamp.js @@ -0,0 +1,12 @@ +function timeStamp(dateTimeISO8601) { + const date = new Date(dateTimeISO8601) + + date.setHours(date.getUTCHours() - 3) + + const timestamp = date.getTime() / 1000 + + return timestamp +} + + +module.exports = timeStamp