Hubspotのコール記録をどうしても抜き出したいんです!と社内で言われたので、
遠い記憶を手繰り寄せながらコードを書いてみました。
今回使用したAPIはHubspot API V3のcallsです。
https://developers.hubspot.com/docs/api/crm/calls
2年ほど前はまだEngagement(コール、ミーティング、コメントなど)はV1しかなかったのですが、
いつの間にかV3がでていました。
そしてV3では「Search」が用意されているため、
簡単にデータを引っ張ってくることができました。
まずは完成品がこちら。
function getEngagement() {
const spreadsheet = SpreadsheetApp.openById("スプレッドシートのシートIDをいれる");
const sheetHubspot = spreadsheet.getSheetByName('タブ名');
//シート全クリア
sheetHubspot.getRange(2,1,9999,20).clear();
//HubspotのAPI接続設定
const API_KEY = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
//更新する項目(列)数
const lastColumn = 5;
//フィルターに使用する日時(UNIXミリ秒)
const date = 1643641200000; //2022-02-01
//取得準備
let ListCalls = [];//リストの配列
let hasMore = true; //追加データが有る場合はtrue
let after = 0; //一度で取得できなかった場合、続きから取得
while(hasMore){
//1秒経過してから処理開始
Utilities.sleep(1000);
//Hubspotからコンタクトを取得
let url = "https://api.hubapi.com/crm/v3/objects/calls/search" + "?hapikey=" + API_KEY;
let headers = {"content-type" : "application/json"}
let payload = {
"filterGroups": [
{
"filters": [
{
"propertyName": "hs_timestamp",
"operator": "GTE",
"value": 1643641200000 //2022-02-01
}
]
}
],
"sorts": ["hs_timestamp"],
"properties": ["hs_call_body,hs_call_disposition,hs_timestamp,hubspot_owner_id"],
"limit": 100,
"after": after
}
payload = JSON.stringify(payload);
let options = {
"method": 'POST',
"headers": headers,
"payload": payload,
"muteHttpExceptions": true
};
let response = UrlFetchApp.fetch(url, options);
//スプレッドシートにマッピング
if(response){
//JSON→配列化
let responseArr = JSON.parse(response.getContentText());
//連想配列からkeyが"results"の中身を取得
let arrContacts = responseArr.results;
//連想配列からkeyが"properties"の中身を配列形式で取得
let arrProperties = arrContacts.map(obj => obj.properties);
//リストの配列
for (const elem of arrProperties) {
//アイテム単位の配列
let ItemCalls = [];
//A_コール担当者
if(elem["hubspot_owner_id"]){
ItemCalls.push(elem["hubspot_owner_id"]);
}
else {
ItemCalls.push("");
}
//B_コール担当者の名前置換用の列
ItemCalls.push("");
//C_コール内容
if(elem["hs_call_body"]){
ItemCalls.push(elem["hs_call_body"]);
}
else {
ItemCalls.push("");
}
//D_コール結果
if(elem["hs_call_disposition"]){
ItemCalls.push(elem["hs_call_disposition"]);
}
else {
ItemCalls.push("");
}
//E_コール日
if(elem["hs_timestamp"]){
ItemCalls.push(elem["hs_timestamp"]);
}
else {
ItemCalls.push("");
}
//配列に格納
ListCalls.push(ItemCalls);
}
//hasMoreがない場合は終了
if ("paging" in responseArr) {
//afterを書き換え
after = responseArr.paging.next.after;
}
else {
hasMore = false;
}
}
//responseがない場合は終了
else {
hasMore = false;
}
}
//シートへの記入
sheetHubspot.getRange(2, 1, ListCalls.length, lastColumn).setValues(ListCalls);
}
それでは解説をしていきたいと思います。
事前準備
シートの定義
const spreadsheet = SpreadsheetApp.openById("スプレッドシートのシートIDをいれる");
const sheetHubspot = spreadsheet.getSheetByName('タブ名');
https://docs.google.com/spreadsheets/d/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/
のxxxxx...に当てはまる部分が「スプレッドシートのシートIDをいれる」に該当します。
そしてタブに任意の名前をつけて、そのタブを更新用のシートにします。
シートの内容クリア
シートクリアは必要に応じて。
//シート全クリア
sheetHubspot.getRange(2,1,9999,20).clear();
HubspotのAPI
Hubspotの設定 > 連携 > APIキーに記載があります。
//HubspotのAPI接続設定
const API_KEY = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
更新する列
今回の取得したいプロパティの数を記載しましょう。
//更新する項目(列)数
const lastColumn = 5;
フィルターに使用する日時
//フィルターに使用する日時(UNIXミリ秒)
const date = 1643641200000; //2022-02-01
Hubspotからの取得
準備
リストにデータを格納し、まとめてスプレッドシートに貼り付ける形式となります。
hasMore
は、後述のwhile
内で利用します。
"paging"
という言葉が配列の中に格納されている場合はafter
(ページ番号)の数字を取得し、存在しない場合はループを終了させる、という役割を果たしています。
//取得準備
let ListCalls = [];//リストの配列
let hasMore = true; //追加データが有る場合はtrue
let after = 0; //一度で取得できなかった場合、続きから取得
//hasMoreがない場合は終了
if ("paging" in responseArr) {
//afterを書き換え
after = responseArr.paging.next.after;
}
else {
hasMore = false;
}
取得
Utilities.sleep(1000);
を入れているのは、ループが高速すぎると制限に引っかかるからです。
while(hasMore){
//1秒経過してから処理開始
Utilities.sleep(1000);
フィルターのかけ方ですが、
"filterGroups": [
{
"filters": [
{
"propertyName": "hs_timestamp",
"operator": "GTE",
"value": 1643641200000 //2022-02-01
}
]
}
],
公式ドキュメントには以下のように記述があります。
複数のフィルタ条件を含めるには、フィルタを filterGroups でグループ化します。
- 複数のフィルタが filterGroup 内に存在するときは、AND演算子で結合されます。
- 複数の filterGroup が含まれる場合、それらはOR演算子を用いて結合されます。
filterGroups は最大3つまで含めることができ、各グループには最大3つのフィルタがあります。たとえば、以下のリクエストは、ファーストネームがAliceで、ラストネームがSmithでない、またはプロパティemailの値を持っているすべてのコンタクトを検索します。
"filterGroups":[
{
"filters":[
{
"propertyName": "firstname",
"operator": "EQ",
"value": "Alice"
}
]
}
]
すなわち、AかつBにしたいときはfilterGroupsの中にfiltersを並列で配置すればよく、
AまたはBにしたいときはfilterGroupesを並列で配置する、ということですね。
OPERATOR | DESCRIPTION |
---|---|
LT | 未満(Less than) |
LTE | 以下(Less than or equal to) |
GT | より大きい(Greater than) |
GTE | 以上(Greater than or equal to) |
EQ | 等しい(Equal to) |
NEQ | 等しくない(Not equal to) |
BETWEEN | の間 |
IN | 特定のリストに含まれる |
NOT_IN | 特定のリストに含まれない |
HAS_PROPERTY | 既知である |
NOT_HAS_PROPERTY | 不明である |
Between
のみhighValue
が必要になります。
"filterGroups":[{
"filters":[
{
"propertyName":"hs_lastmodifieddate",
"operator":"BETWEEN",
"highValue": "1642672800000",
"value":"1579514400000"
}
]
}
]
公式ドキュメントはこちら↓
フィルター内に登場する日時の指定は、UNIXミリ秒で指定しています。
"value": 1643641200000 //2022-02-01
UNIXミリ秒は以下のサイトから調べることが可能です。
ざっとポイントを解説してみましたが、わからないことがあればコメントなどで気軽にご質問ください。
必要な情報など追記して、よりわかりやすくしていきたいと思います。
ちなみに前回の記事はこちらです。合わせて参考にしてみてください。