Integration関数の設定
次に、先ほど初期化したIntegration関数を設定して、eコマースボットの実行ロジックのコーディングを開始します。
関数のディレクトリfunctions/ecommerce_functionには以下が含まれています:
- index.jsメイン関数ファイル
- ConvoKraftハンドラー関数ファイル
- catalyst-config.json設定ファイル
- Nodeモジュール
- package.jsonおよびpackage-lock.json依存関係ファイル
ハンドラー関数execute.js、fallback.js、failure.js、welcome.js、およびメインのindex.jsファイルにコードを追加します。
以下のコードをお好みのIDEを使用して、それぞれのファイルに注意深くコピー&ペーストして保存してください。
import logger from "./logger.js";
import fetch from "node-fetch";
import catalyst from "zcatalyst-sdk-node";
// GETリクエストを実行する関数
async function get(url, headers) {
const response = await fetch(url, {
method: ‘GET’,
headers: headers
});
return await response.json();
}
function getRandomNumber(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// POSTリクエストを実行する関数
async function post(url, headers, body) {
const response = await fetch(url, {
method: ‘POST’,
headers: headers,
body: JSON.stringify(body)
});
return await response.json();
}
// PUTリクエストを実行する関数
async function put(url, headers, body) {
const response = await fetch(url, {
method: ‘PUT’,
headers: headers,
body: JSON.stringify(body)
});
return await response.json();
}
async function getCRMToken(request)
{
const catalystApp = catalyst.initialize(request);
var connector = catalystApp.connection({
CRMTOKEN: {
client_id: `${process.env.client_id}`,
client_secret: `${process.env.client_secret}`,
auth_url: `${process.env.auth_url}`,
refresh_url: `${process.env.refresh_url}`,
refresh_token: `${process.env.refresh_token}`
}
}).getConnector(‘CRMTOKEN’);
let accessTokenResponse=await connector.getAccessToken().then((accessToken) => {
//console.log(“Inside accesstoken”+accessToken);
return accessToken;
});
return accessTokenResponse;
}
// URL呼び出し関数
async function invokeURL(options) {
if (options.type === ‘GET’) {
const response = await get(options.url, options.headers);
return response;
} else if (options.type === ‘POST’) {
const response = await post(options.url, options.headers, options.body);
return response;
} else if (options.type === ‘PUT’) {
const response = await put(options.url, options.headers, options.body);
return response;
} else {
throw new Error(`Unsupported HTTP method: ${options.type}`);
}
}
// execute機能を処理する
export default async function handleExecute(request) {
logger.info(‘Handling execute request with params : ‘+JSON.stringify(request));
logger.info(‘Action NameSpace : ‘+request.action.namespace);
logger.info(‘Params : ‘+JSON.stringify(request.params));
logger.info(‘Broadcast : ‘+JSON.stringify(request.broadcast));
const result = {};
const cardlist = [];
switch(request.action.namespace)
{
case “listofitems_2”:
logger.info(‘Inside NameSpace ‘+request.action.namespace);
const executeViewItemsLogic = async () => {
//let zohocrmapiurl=process.env.crmapiurl;
console.log("ENV VARIABLE "+JSON.stringify(process.env));
//console.log("Client ID"+`${process.env.client_id}`);
const token=await getCRMToken(request);
//console.log("ACCESSTOKEN RESP : "+token);
const accessToken = 'Zoho-Oauthtoken ' + token;
logger.info('Access Token : '+accessToken);
const header_data = new Map();
header_data.set('Content-Type', 'application/json');
header_data.set('Authorization', accessToken);
const CRMresponse = await invokeURL({
url: 'https://'+process.env.crmapiurl+'/crm/v2/EcomProducts?fields=Name,Product_image_url,Price,Inventory_Count,Product_image',
type: 'GET',
headers: header_data
});
logger.info(“Response for list of items “+ JSON.stringify(CRMresponse));
const productNote = {
“type”: “note”,
“content”: “List of items available are listed as follows”
};
cardlist.push(productNote);
for (const record of CRMresponse.data) {
logger.info('RECPORD INFO : '+JSON.stringify(record));
const name = record.Name;
const price = record.Price;
const product_id = record.id;
const image = record.Product_image;
const inventory = record.Inventory_Count;
const listItem = {
"type": "list",
"format": "bullet",
"elements": [
{ "label": `Name : ${name}` },
{ "label": `Total Price : $ ${price}` },
{ "label": `Product ID : ${product_id}` },
{ "label": `Stock : ${inventory}` }
]
};
const productPic = {
"type": "image",
"content": image
};
cardlist.push(productPic);
cardlist.push(listItem);
logger.info('Card List : : : :'+JSON.stringify(cardlist));
}
result.card= cardlist;
result.message= "List of Objects in response";
logger.info('Result Obj inside the : : : ' + result);
logger.info('Result OF MAP : : : :'+JSON.stringify(result));
};
await executeViewItemsLogic();
logger.info(‘Result Obj before return : : : ’ + JSON.stringify(result));
return result;
break;
case “vieworderdetails_3”:
logger.info(‘Inside NameSpace ’ + request.action.namespace);
const executeViewOrderLogic = async () => {
const token=await getCRMToken(request);
var cxemail=request.params.cxemail;
const accessToken = ‘Zoho-Oauthtoken ’ + token;
logger.info(‘Access Token: ’ + accessToken);
const header_data = new Map();
header_data.set(‘Content-Type’, ‘application/json’);
header_data.set(‘Authorization’, accessToken);
let resp={}
try{
resp = await invokeURL({
url: ‘https://’+process.env.crmapiurl+’/crm/v4/Orders/search?criteria=((Email:equals:’ + cxemail + ‘))’,
type: ‘GET’,
headers: header_data
});
}catch(er)
{
logger.info(‘ERROR WHILE CRM DATA SEARCH ’ + er);
}
logger.info(‘Response: ’ + JSON.stringify(resp));
let items_count = 0;
for (const order of resp.data) {
items_count++;
logger.info('Inside Resp Iterator - Item count : '+items_count+' Order details : '+JSON.stringify(order));
const order_id = order.id;
const CRMresponse = await invokeURL({
url: 'https://'+process.env.crmapiurl+'/crm/v3/Orders/' + order_id,
type: 'GET',
headers: header_data
});
logger.info("CRMRESPONSE : : : :"+JSON.stringify(CRMresponse.data[0].product_image_url));
const orderid = order.OrderID;
const name = order.Name;
const contact = order.Contact_No;
const email = order.Email;
const product_name = order.Product_Name;
const price = order.Price;
const delivery_date = order.Expected_Delivery_Date;
const delivery_address = order.Delivery_Address;
const order_status = order.Order_Status;
const listItem = {
"type": "list",
"format": "bullet",
"elements": [
{ "label": "Name: " + name },
{ "label": "Contact No: " + contact },
{ "label": "Email ID: " + email },
{ "label": "Product Name: " + product_name },
{ "label": "Total Price: $" + price },
{ "label": "Expected Delivery Date: " + delivery_date },
{ "label": "Delivery Address: " + delivery_address },
{ "label": "Order Status: " + order_status }
]
};
const productNote = {
"type": "note",
"content": items_count + ") Your order details for order id : " + orderid + " are listed as follows: "
};
const productPic = {
"type": "image",
"content": CRMresponse.data[0].product_image_url
};
cardlist.push(productNote);
cardlist.push(productPic);
cardlist.push(listItem);
logger.info("CARD LIST : : : "+JSON.stringify(cardlist));
}
result.card = cardlist;
// broadcastを定義する
result.broadcast = {
"cxemail": cxemail
};
result.message = "The Shoppers Stores. You don't have an order.";
logger.info("Result JSON : : : "+JSON.stringify(result));
};
await executeViewOrderLogic();
//logger.info(‘Result Obj before return: ’ + JSON.stringify(result));
return result;
break;
case “placeanorder_4”:
logger.info(‘Inside NameSpace ’ + request.action.namespace);
const executePlaceOrderLogic = async () => {
const placeorder = {};
const token=await getCRMToken(request);
const accessToken = ‘Zoho-Oauthtoken ’ + token;
logger.info(‘Access Token: ’ + accessToken);
const header_data = new Map();
header_data.set(‘Content-Type’, ‘application/json’);
header_data.set(‘Authorization’, accessToken);
try {
const CRMresponse = await invokeURL({
url: 'https://'+process.env.crmapiurl+'/crm/v2/EcomProducts/' + request.params.productid,
type: 'GET',
headers: header_data
});
logger.info('CRM Response: ' + JSON.stringify(CRMresponse));
if (CRMresponse.data == null && CRMresponse.status != 'error') {
const stock_count = 0;
logger.info('Empty response. Stock count reset.');
placeorder.put('Stock', 'Empty');
} else {
const recordid = CRMresponse.data[0].id;
let stock_count = CRMresponse.data[0].Inventory_Count;
const imageurl = CRMresponse.data[0].Product_image;
const productname = CRMresponse.data[0].Name;
const price = CRMresponse.data[0].Price;
logger.info(recordid+” “+stock_count+” “+imageurl+” “+productname+” “+price);
if (stock_count > 0) {
stock_count = stock_count - 1;
const reduceStock = {
id: recordid,
Inventory_Count: stock_count
};
const updatedata = [reduceStock];
const updatedatamap = {
data: updatedata
};
const updateResp = await invokeURL({
url: ‘https://’+process.env.crmapiurl+’/crm/v2/EcomProducts’,
type: ‘PUT’,
body: updatedatamap,
headers: header_data
});
logger.info(‘Update Response: ’ + JSON.stringify(updateResp));
placeorder.Name = productname + ’ ’ + request.params.cxdate; // ドット記法を使用して値を代入する
placeorder.Product_Name = productname; // ドット記法を使用して値を代入する
placeorder.Email = request.params.cxemail; // ドット記法を使用して値を代入する
placeorder.Delivery_Address = request.params.cxaddr; // ドット記法を使用して値を代入する
placeorder.Expected_Delivery_Date = new Date(request.params.cxdate).toLocaleDateString(); // ドット記法を使用して値を代入する
placeorder.product_image_url = imageurl; // ドット記法を使用して値を代入する
placeorder.Price = price; // ドット記法を使用して値を代入する
const orderID = request.params.productid + ‘_’ + getRandomNumber(1111111, 9999999);
//placeorder.put(‘OrderID’, orderID);
placeorder.OrderID = orderID;
placeorder.Order_Status = ‘Reviewing the Order’; // ドット記法を使用して値を代入する
const insertdatamap = {
data: [placeorder]
};
logger.info(“INSERT MAP : : :"+JSON.stringify(insertdatamap));
const insertResp = await invokeURL({
url: ‘https://’+process.env.crmapiurl+’/crm/v2/Orders’,
type: ‘POST’,
body: insertdatamap,
headers: header_data
});
logger.info(‘Insert Response: ’ + JSON.stringify(insertResp));
result.message =
'Your order for the product - ' +
CRMresponse.data[0].Name +
' is placed successfully. It will be tried to be delivered on ' +
request.params.cxdate +
' to ' +
request.params.cxaddr +
'. Our Sales team will reach out to you via email to discuss COD or prepayment before delivery.';
} else {
result.message = 'The item is out of stock. Please retry after some days as we restock.';
}
}
} catch (e) {
logger.info(e.lineNo);
logger.info(e.message);
logger.info(e);
result.message = 'There was some issue during the order. Please contact the support team. They shall assist in placing this order for you.';
}
result.broadcast = { cxemail: request.params.cxemail };
};
await executePlaceOrderLogic();
return result;
break;
case “changedeliveryaddress”: // 配送先住所変更の新しいケース
logger.info(‘Inside NameSpace ’ + request.action.namespace);
const executeChangeDeliveryAddressLogic = async () => {
const token=await getCRMToken(request);
const cxorderid = request.params.cxorderid;
const cxaddr = request.params.cxaddr;
const cxemailid = request.params.cxemail;
const accessToken = ‘Zoho-Oauthtoken ’ + token;
logger.info(‘Access Token: ’ + accessToken);
const header_data = new Map();
header_data.set(‘Content-Type’, ‘application/json’);
header_data.set(‘Authorization’, accessToken);
let searchOrderResp={};
try{
searchOrderResp = await invokeURL({
url: ‘https://’+process.env.crmapiurl+’/crm/v4/Orders/search?criteria=((OrderID:equals:’ + cxorderid + ‘))’,
type: ‘GET’,
headers: header_data
});
}catch(e)
{
logger.info(“ERROR DURING SEARCH ORDER : : “+e);
searchOrderResp={};
}
logger.info(“Result of search Order: : : “+JSON.stringify(searchOrderResp));
if(searchOrderResp.data!=undefined)
{
const recordId = searchOrderResp.data[0].id;
//logger.info("Result of search Order: : : "+JSON.stringify(searchOrderResp.data[0]));
const recordEmail=searchOrderResp.data[0].Email;
const recordorderId=searchOrderResp.data[0].OrderID;
if(cxemailid == recordEmail && cxorderid == recordorderId)
{
const updateData = [
{
id: recordId,
Delivery_Address: cxaddr
}
];
const updateDataMap = {
data: updateData
};
const updateResp = await invokeURL({
url: 'https://'+process.env.crmapiurl+'/crm/v2/Orders',
type: 'PUT',
body: updateDataMap,
headers: header_data
});
logger.info('Update Response: ' + JSON.stringify(updateResp));
result.message = 'Delivery address updated successfully.';
}
else{
result.message=‘Given Order ID or Email is not proper. Please check and retry’;
}
}
else
{
result.message=‘Given Order ID or Email is not proper. Please check and retry’;
}
};
await executeChangeDeliveryAddressLogic();
return result;
break;
case “rescheduleorderdate”:
logger.info(‘Inside NameSpace ’ + request.action.namespace);
const executeChangeDeliveryDateLogic = async () => {
const token=await getCRMToken(request);
const accessToken = ‘Zoho-Oauthtoken ’ + token;
logger.info(‘Access Token: ’ + accessToken);
const header_data = new Map();
header_data.set(‘Content-Type’, ‘application/json’);
header_data.set(‘Authorization’, accessToken);
const cxemailid = request.params.cxemail;
const cxorderid = request.params.cxorderid;
let searchOrderResp={};
try{
searchOrderResp = await invokeURL({
url: ‘https://’+process.env.crmapiurl+’/crm/v2/Orders/search?criteria=((OrderID:equals:’ + request.params.cxorderid + ‘))’,
type: ‘GET’,
headers: header_data
});
}catch(e)
{
searchOrderResp={};
}
if(searchOrderResp.data!=undefined){
logger.info(“Result of search Order: : : “+JSON.stringify(searchOrderResp.data[0]));
const recordId = searchOrderResp.data[0].id;
const recordEmail=searchOrderResp.data[0].Email;
const recordorderId=searchOrderResp.data[0].OrderID;
if(cxemailid == recordEmail && cxorderid == recordorderId)
{
const datemap = {
id: recordId,
Expected_Delivery_Date: new Date(request.params.cxdate).toLocaleDateString()
};
const updatedata = [datemap];
const updatedatamap = {
data: updatedata
};
logger.info("update Map : : : "+JSON.stringify(updatedatamap));
const updateResp = await invokeURL({
url: 'https://'+process.env.crmapiurl+'/crm/v2/Orders',
type: 'PUT',
body: updatedatamap,
headers: header_data
});
logger.info(“updateResponse : : : “+JSON.stringify(updateResp));
result.broadcast = { cxemail: request.params.cxemail };
result.message = ‘Your order has been rescheduled successfully.’;
}else{
result.message=‘Given Order ID or Email is not proper. Please check and retry’;
}
}
else{
result.message=‘Given Order ID or Email is not proper. Please check and retry’;
}
};
await executeChangeDeliveryDateLogic();
return result;
break;
default :
result.message=‘Given request is invalid. Please check and retry’;
return result;
break;
}
//return response;
}
import logger from "./logger.js";
// welcomeの機能を処理する
export default function handleWelcome() {
logger.info('Handling welcome request');
return {
"welcome_response": {
"message": "Welcome to your Bot. Please ask your queries"
}
};
}
import logger from "./logger.js";
// failureの機能を処理する
export default function handleFailure() {
logger.info(‘Handling failure request’);
const result = {
“message”: “Something went wrong while checking your query. Please try again later.”
};
return JSON.stringify(result);
}
import logger from "./logger.js";
// fallbackの機能を処理する
export default function handleFallback() {
logger.info('Handling fallback request');
const result = {
"message": "Sorry couldn't get that."
};
return JSON.stringify(result);
}
import logger from './logger.js';
import IntegResponse from './integ-response.js';
import handleWelcome from './welcome.js';
import handleFallback from './fallback.js';
import handleExecute from './execute.js';
import handleFailure from './failure.js';
export default async (request, response) => {
let jsonResponse = {};
try {
logger.info(‘REQUEST FLOW FOR : : : ’ + request.todo);
switch (request.todo) {
case “welcome”:
jsonResponse = handleWelcome();
break;
case “execute”:
jsonResponse = await handleExecute(request);
break;
case “fallback”:
jsonResponse = handleFallback();
break;
case “failure”:
jsonResponse = handleFailure();
break;
default:
jsonResponse = JSON.stringify({
“message”: “Error Trying to parse your details”
});
}
} catch (err) {
logger.error(‘Error while executing handler: ’ + err);
logger.error(‘REQUEST OBJECT: ’ + JSON.stringify(request));
jsonResponse = JSON.stringify({
"message": "Error Trying to parse your details"
});
}
// レスポンスはIntegResponseオブジェクトにカプセル化する必要があります
response.end(new IntegResponse(jsonResponse));
};
各関数ファイルとそのロジックを簡単に確認しましょう:
- execute.js:このファイルには、eコマースボットのすべてのアクションのメイン実行ロジックが含まれています。これらのアクションはすべて、設定された環境変数を使用してZoho CRMアカウントを認証し、必要な詳細を取得するか、以下に示すアクションを実行します。ecommerce_function Catalyst関数の環境変数は、このステップで関数をコンソールにデプロイした後、Catalystコンソールから直接設定します。
-
listofitems_2:このアクションはAPIリクエストを開始し、カスタムCRMモジュールEcomProductsに対してGETメソッドをトリガーし、商品名、商品ID、商品画像URL、価格、在庫数を取得します。
-
placeanorder_4:このアクションは、ユーザーから商品IDを入力として受け取ります。次に、EcomProducts CRMモジュールでその商品の在庫状況を確認します。在庫がある場合、ボットはユーザーのメールアドレス、希望配送日、配送先住所の入力を求めます。これらの詳細が入力されると、ボットはAPIリクエストを開始し、カスタムCRMモジュールOrdersに対してPOSTメソッドをトリガーして注文を作成します。注文が正常に完了すると、チャットボットを通じてユーザーに通知されます。商品の在庫がない場合は、その旨がユーザーに通知されます。
-
vieworderdetails_3:このアクションはZoho CRM Search APIをトリガーして、カスタムモジュールOrdersで検索操作を実行します。顧客名、連絡先番号、メールID、商品名、商品価格、商品画像URL、配送予定日、配送先住所、注文ステータスの詳細がレスポンスとして取得されます。
-
changedeliveryaddress:このアクションは、注文に関連付けられたメールID、注文ID、および配送先住所を入力として受け取ります。カスタムモジュールOrdersに対してZoho CRM Search APIがトリガーされます。指定されたメールIDと注文IDが検証され、カスタムモジュールOrdersに対してPUTメソッドを実行することで、新しい住所が注文に更新されます。
-
rescheduleorderdate:このアクションは、注文に関連付けられたメールIDと注文IDを入力として受け取ります。カスタムモジュールOrdersに対してZoho CRM Search APIがトリガーされ、指定された注文IDで検索操作が実行されます。メールIDと注文IDが検証され、カスタムモジュールOrdersに対してPUTメソッドを再度実行することで、次の可能な日付が配送日として更新されます。
上記のアクション名は、コンソールで作成されたアクションのnamespaceです。コードの82行目、145行目、238行目、337行目、407行目のswitchケースlistofitems_2、vieworderdetails、placeanorder、changedeliveryaddress、rescheduleordertolaterdateについて、それぞれのアクションに生成されたnamespaceを必ず更新してください。
アクションのnamespaceは、以下のスクリーンショットのようにコンソールから取得できます。
-
welcome.js:このファイルでは、ユーザーとの新しい会話を開始する際に表示される挨拶メッセージを設定しています。
-
Fallback.js:このファイルでは、ボットがユーザーのメッセージを理解できなかった場合に表示されるメッセージを設定しています。この場合、ユーザーが有効なレスポンスを入力してボットが理解するまで、このレスポンスが会話に表示されます。
-
failure.js:このファイルでは、ビジネスロジックの実行中に例外が発生した場合にユーザーに表示されるメッセージを設定しています。
-
index.js:これは関数のエントリーポイントであり、ボットの会話中のtodoプロンプトに基づいて、execute、welcome、fallback、failureなどの対応するハンドラーファイルをマッピングします。
最終更新日 2026-03-05 11:43:24 +0530 IST
