Skip to content

Commit

Permalink
Caller id match (#156)
Browse files Browse the repository at this point in the history
* Add events for caller id matching

* Add caller id match payload

* Add debugMessageType argument to logging

* Add incomingCall event handler to README

* Log last sync event
  • Loading branch information
esme authored Nov 14, 2023
1 parent f69179c commit 928f8e2
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 19 deletions.
76 changes: 76 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,12 @@ const options = {
},
onUpdateEngagementFailed: event => {
/* HubSpot has failed to update an engagement for this call. */
},
onCallerIdMatchSucceeded: event => {
/* HubSpot has fetched caller id matches for this call. */
},
onCallerIdMatchFailed: event => {
/* HubSpot has failed to fetch caller id matches for this call. */
}
onVisibilityChanged: event => {
/* Call widget's visibility is changed. */
Expand Down Expand Up @@ -217,6 +223,24 @@ extensions.outgoingCall(callInfo);
</p>
</details>

<details>
<summary>incomingCall</summary>
<p>

```ts
// Sends a message to notify HubSpot that an outgoing call has started.
const callInfo = {
fromNumber: string, // Required: The caller's number
toNumber: string, // Required: The recipient's number
createEngagement: boolean, // Whether HubSpot should create an engagement for this call
};
extensions.incomingCall(callInfo);
```

</p>
</details>

<details>
<summary>callAnswered</summary>
<p>
Expand Down Expand Up @@ -408,6 +432,58 @@ onDialNumber(data) {
</p>
</details>

<details>
<summary>onCallerIdMatchSucceeded</summary>
<p>

```js
// Message indicating that HubSpot has updated an engagement
onCallerIdMatchSucceeded(data) {
const {
callerIdMatches: (ContactIdMatch | CompanyIdMatch)[];
} = data;
...
}
type ObjectCoordinate = {
portalId: number;
objectTypeId: string;
objectId: number;
}
type ContactIdMatch = {
callerIdType: 'CONTACT';
objectCoordinates: ObjectCoordinate;
firstName: string;
lastName: string;
email: string;
}
type CompanyIdMatch = {
callerIdType: 'COMPANY';
objectCoordinates: ObjectCoordinate;
name: string;
}
```
</p>
</details>

<details>
<summary>onCallerIdMatchFailed</summary>
<p>

```js
// Message indicating that HubSpot has failed to update an engagement
onCallerIdMatchFailed(data) {
const {
error: { message: string }
} = data;
...
}
```
</p>
</details>

<details>
<summary>onVisibilityChanged</summary>
<p>
Expand Down
44 changes: 36 additions & 8 deletions demos/demo-minimal-js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import { errorType, callEndStatus } from "../../src/Constants";
// import CallingExtensions, { Constants } from "@hubspot/calling-extensions-sdk";
// const { errorType, callEndStatus } = Constants;

const state = {
export const state = {
engagementId: 0,
phoneNumber: "+1234",
toNumber: "+1234",
fromNumber: "+123456",
userAvailable: false,
incomingContactName: "",
};

const sizeInfo = {
Expand Down Expand Up @@ -53,7 +55,7 @@ const cti = new CallingExtensions({
},
onDialNumber: (data, rawEvent) => {
const { phoneNumber } = data;
state.phoneNumber = phoneNumber;
state.toNumber = phoneNumber;
},
onEngagementCreated: (data, rawEvent) => {
const { engagementId } = data;
Expand All @@ -75,6 +77,32 @@ const cti = new CallingExtensions({
state.engagementId = engagementId;
},
onUpdateEngagementFailed: (data, rawEvent) => {},
onCallerIdMatchSucceeded: (data, rawEvent) => {
const { callerIdMatches } = data;
if (callerIdMatches.length) {
const firstCallerIdMatch = callerIdMatches[0];
if (firstCallerIdMatch.callerIdType === "CONTACT") {
state.incomingContactName = `${firstCallerIdMatch.firstName} ${firstCallerIdMatch.lastName}`;
} else if (firstCallerIdMatch.callerIdType === "COMPANY") {
state.incomingContactName = firstCallerIdMatch.name;
}
cti.logDebugMessage({
message: `Incoming call from ${state.incomingContactName} ${state.fromNumber}`,
type: `${callerIdMatches.length} Caller ID Matches`,
});
return;
}
cti.logDebugMessage({
message: `Incoming call from ${state.fromNumber}`,
type: "No Caller ID Matches",
});
},
onCallerIdMatchFailed: (data, rawEvent) => {
cti.logDebugMessage({
message: `Incoming call from ${state.fromNumber}`,
type: "Caller ID Match Failed",
});
},
},
});

Expand Down Expand Up @@ -131,9 +159,9 @@ export function userUnavailable() {
export function incomingCall() {
window.setTimeout(() => {
cti.incomingCall({
createEngagement: "true",
fromNumber: "+123",
toNumber: state.phoneNumber,
createEngagement: true,
fromNumber: state.fromNumber,
toNumber: state.toNumber,
});
}, 500);
disableButtons([OUTGOING_CALL, INCOMING_CALL, USER_UNAVAILABLE]);
Expand All @@ -143,8 +171,8 @@ export function incomingCall() {
export function outgoingCall() {
window.setTimeout(() => {
cti.outgoingCall({
createEngagement: "true",
phoneNumber: state.phoneNumber,
createEngagement: true,
phoneNumber: state.toNumber,
});
}, 500);
disableButtons([OUTGOING_CALL, INCOMING_CALL, USER_UNAVAILABLE]);
Expand Down
24 changes: 20 additions & 4 deletions src/CallingExtensions.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
"use es6";

import IFrameManager from "./IFrameManager";
import { messageType, errorType } from "./Constants";
import { messageType, debugMessageType, errorType, VERSION } from "./Constants";

const prefix = `[calling-extensions-sdk@${VERSION}]`;

/**
* @typedef {Object} EventHandlers
Expand Down Expand Up @@ -129,6 +131,10 @@ class CallingExtensions {
this.iFrameManager.sendMessage(message);
}

logDebugMessage({ message, type = debugMessageType }) {
this.iFrameManager.logDebugMessage(prefix, type, message);
}

onMessageHandler(event) {
const { type, data } = event;
const { eventHandlers } = this.options;
Expand Down Expand Up @@ -174,14 +180,24 @@ class CallingExtensions {
handler = onCreateEngagementFailed;
break;
}
case messageType.UPDATE_ENGAGEMENT_SUCCEEDED: {
const { onUpdateEngagementSucceeded } = eventHandlers;
handler = onUpdateEngagementSucceeded;
break;
}
case messageType.UPDATE_ENGAGEMENT_FAILED: {
const { onUpdateEngagementFailed } = eventHandlers;
handler = onUpdateEngagementFailed;
break;
}
case messageType.UPDATE_ENGAGEMENT_SUCCEEDED: {
const { onUpdateEngagementSucceeded } = eventHandlers;
handler = onUpdateEngagementSucceeded;
case messageType.CALLER_ID_MATCH_SUCCEEDED: {
const { onCallerIdMatchSucceeded } = eventHandlers;
handler = onCallerIdMatchSucceeded;
break;
}
case messageType.CALLER_ID_MATCH_FAILED: {
const { onCallerIdMatchFailed } = eventHandlers;
handler = onCallerIdMatchFailed;
break;
}
default: {
Expand Down
16 changes: 16 additions & 0 deletions src/Constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
const { version } = require("../package.json");
export const VERSION = version;

export const debugMessageType = {
FROM_HUBSPOT: "From HubSpot",
TO_HUBSPOT: "To HubSpot",
GENERIC_MESSAGE: "Generic Message",
};

export const messageType = {
CALL_ANSWERED: "CALL_ANSWERED",
CALL_COMPLETED: "CALL_COMPLETED",
Expand Down Expand Up @@ -34,6 +40,8 @@ export const messageType = {
USER_UNAVAILABLE: "USER_UNAVAILABLE",
UPDATE_ENGAGEMENT_FAILED: "UPDATE_ENGAGEMENT_FAILED",
UPDATE_ENGAGEMENT_SUCCEEDED: "UPDATE_ENGAGEMENT_SUCCEEDED",
CALLER_ID_MATCH_SUCCEEDED: "CALLER_ID_MATCH_SUCCEEDED",
CALLER_ID_MATCH_FAILED: "CALLER_ID_MATCH_FAILED",
VISIBILITY_CHANGED: "VISIBILITY_CHANGED",
};

Expand Down Expand Up @@ -89,3 +97,11 @@ export const callEndStatus = {
INTERNAL_BUSY,
INTERNAL_NO_ANSWER,
};

export const CONTACT = "CONTACT";
export const COMPANY = "COMPANY";

export const callerIdTypes = {
CONTACT,
COMPANY,
};
17 changes: 10 additions & 7 deletions src/IFrameManager.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use es6";

import { messageType, VERSION } from "./Constants";
import { messageType, debugMessageType, VERSION } from "./Constants";

const prefix = `[calling-extensions-sdk@${VERSION}]`;
/*
Expand Down Expand Up @@ -77,9 +77,7 @@ class IFrameManager {
}

static createIFrame(iFrameOptions, onLoadCallback) {
const {
src, width, height, hostElementSelector,
} = iFrameOptions;
const { src, width, height, hostElementSelector } = iFrameOptions;

if (!src || !width || !height || !hostElementSelector) {
throw new Error(
Expand Down Expand Up @@ -152,7 +150,11 @@ class IFrameManager {
const { type } = message;
if (type !== messageType.SYNC && !this.isReady) {
// Do not send a message unless the iFrame is ready to receive.
console.warn(prefix, "iFrame not initialized to send a message within HubSpot", message);
console.warn(
prefix,
"iFrame not initialized to send a message within HubSpot",
message,
);
return;
}

Expand All @@ -170,7 +172,7 @@ class IFrameManager {
messageId,
});

this.logDebugMessage(prefix, "To HubSpot", type, message);
this.logDebugMessage(prefix, debugMessageType.TO_HUBSPOT, type, message);
this.destinationWindow.postMessage(newMessage, this.destinationHost);
}

Expand All @@ -191,6 +193,7 @@ class IFrameManager {

const { hostUrl } = event.data;
this.destinationHost = hostUrl || this.destinationHost;
this.logDebugMessage(prefix, debugMessageType.FROM_HUBSPOT, type, data);
this.sendMessage(message);
this.onReady();
}
Expand All @@ -214,7 +217,7 @@ class IFrameManager {
return;
}

this.logDebugMessage(prefix, "From HubSpot", type, { data });
this.logDebugMessage(prefix, debugMessageType.FROM_HUBSPOT, type, data);
if (this.instanceRegexp.test(messageId)) {
// This is a response to some message generated by HubSpot
const callBack = this.callbacks[messageId];
Expand Down

0 comments on commit 928f8e2

Please sign in to comment.