// tslint:disable-next-line:no-var-requires const db = require('./db.json'); const zlib = require("zlib"); const request = require('request'); const express = require('express'); const app = express(); // tslint:disable-next-line:no-var-requires // const apis = require('./api') // tslint:disable-next-line:no-var-requires const filters = require('./filters.json'); // tslint:disable-next-line:no-var-requires const XLSX = require('xlsx'); // tslint:disable-next-line:no-var-requires const axios = require('axios'); let suggestion = []; let endString; // tslint:disable-next-line:no-var-requires const locationLatLong = require('./mylocation.json'); // tslint:disable-next-line:no-var-requires const locations = require('./locations.json'); // tslint:disable-next-line:no-var-requires const iconUrl = require('./iconUrl.json'); // tslint:disable-next-line:no-var-requires const MongoClient = require('mongodb').MongoClient; const pipeline = [{ $project: { documentKey: false } }]; let collection; let responseLibraryData; let intentMapData; const headers = { 'Content-Type': 'application/json', 'auth_id': '8E8CA351-110E-4272-8A15-DD26E3B3E8C5', 'accept-encoding': 'gzip,deflate' } // const headers = { // 'auth_id': 'EB884785-82B8-4F20-8264-D629D51D99BB', // 'Content-Type': 'application/json' // } const mapJson = { "sharingType": "RoomType", "roomSubType": "RoomSubType", "roomClass": 'RoomClass', "furnishStatus": "FurnishStatus" } const coliveBaseUrl = 'http://staging.colive.in/webservices/CRMapi/Chatbot/' const mlSuggestionsBaseUrl = 'https://colive-sales-brain.azurewebsites.net/' // https://colive-filter-suggestion.herokuapp.com/ // const coliveBaseUrl = 'https://www.colive.in/webservices/CRMapi/Chatbot/' const apis = { "ml_analyze": "https://review-analytics-web-backend-filter-prediction.azurewebsites.net/analyze", "train_sequence": mlSuggestionsBaseUrl + 'train_suggestion', "lead_insert": coliveBaseUrl + 'LandingPageLeadInsert', "lead_update": coliveBaseUrl + 'LandingPageLeadUpdate', "ticket_create": coliveBaseUrl + 'ServiceRequestInsert', "ticket_update": coliveBaseUrl + 'ServiceRequestStatusUpdate_V2', "ticket_rating": coliveBaseUrl + 'ServiceRequestRatingUpdate_V2', "ticket_history": coliveBaseUrl + 'ServiceRequestSelectAllById', "crm_master": coliveBaseUrl + 'GetServiceRequestMasters', getPropertyByLocationLink: coliveBaseUrl + 'GetPropertyDetailsById', getRoomWiseDetailFromPropertyId: coliveBaseUrl + 'GetPropertyAvailablityDetails', searchPropertyDetails: coliveBaseUrl + 'GetPropertySearchDetails', getFilterSuggestionsFromMl: mlSuggestionsBaseUrl + 'get_filter_suggestion' } MongoClient.connect("mongodb+srv://test1:test1@test.ln42j.azure.mongodb.net/test?retryWrites=true&w=majority", { useNewUrlParser: true }, { useUnifiedTopology: true }, (err, database) => { if (err) throw err; const db1 = database.db("test"); // Show database docs for debugging collection = db1.collection("testCollection"); const collectionIntentMap = db1.collection("intentMap"); collection.find({}).toArray((err, result) => { if (err) throw err; responseLibraryData = result; // database.close(); }); collectionIntentMap.find({}).toArray((err, result) => { if (err) throw err; intentMapData = result; }); const changeStream = db1.collection('testCollection').watch(pipeline); const changeStreamIntentMap = db1.collection('intentMap').watch(pipeline); // tslint:disable-next-line:only-arrow-functions changeStream.on("change", function (change) { // tslint:disable-next-line:no-shadowed-variable collection.find({}).toArray((err, result) => { if (err) throw err; responseLibraryData = result; }); // tslint:disable-next-line:no-console // console.log("fullDocument", change); }); // tslint:disable-next-line:only-arrow-functions changeStreamIntentMap.on("change", function (change) { // tslint:disable-next-line:no-shadowed-variable collectionIntentMap.find({}).toArray((err, result) => { if (err) throw err; intentMapData = result; }); }); // const workbook = XLSX.readFile(__dirname + '/Colive Training.xlsx'); // const sheetNameList = workbook.SheetNames; // responseLibraryData = XLSX.utils.sheet_to_json(workbook.Sheets[sheetNameList[0]]); // setTimeout( () => { // collection.insertMany(responseLibraryData, function (err) { // // assert.ifError(err); // }); // }, 1000); }); app.listen(3000) async function responseLibrary(obj) { let responseData; const requestType = obj.type; if (requestType === 'normal') { responseData = await responseGenerator(obj) } else if (requestType === 'smallTalk') { responseData = await smallTalkResponseGenerator(obj) } else if (requestType === 'mlNormal') { responseData = await mlResponseGenerator(obj) } else { responseData = await inputErrorResponseGenerator(obj) } return JSON.stringify(responseData); } async function getResponseAsItIs(intentName){ } async function getPropertyDetail(obj) { let propertyArray; const filterObject = {}; let queryEntityValues; let queryEntityFlag = false; for (const element of obj) { const key = Object.keys(element)[0]; const value = element[key]; const entityValue = value.listValue['value']; if (key === 'propertyName') { const propertyName = entityValue; propertyArray = propertyName[0].split(' ') propertyArray = propertyArray.splice(0, 2) propertyArray = propertyArray.join("_") } else if (key === 'queryEntity') { queryEntityFlag = true; queryEntityValues = entityValue; } else { filterObject[key] = entityValue; } } const propertyElement = await getPropertyElement(propertyArray) let isFilterTrue = false; const filterProperty = []; if (propertyElement) { if (propertyElement.propertyDetails.Data && propertyElement.propertyDetails.Data.length) { for (const property of propertyElement.propertyDetails.Data) { // tslint:disable-next-line:forin for (const key in filterObject) { const filterKey = mapJson[key]; let val = filterObject[key][0] if (key === 'sharingType') { val = val + ' sharing' } if (filterKey) { if (property[filterKey]) { if (property[filterKey].toLowerCase() === val.toLowerCase()) { isFilterTrue = true; filterProperty.push(property) } } } } } } } const responseObject = []; if (isFilterTrue) { const propertyResponse = []; const respOBJ = { title: propertyElement.propertyName.trim(), imageURL: propertyElement.imageName, description: [ { informationType: 'location', displayValue: propertyElement.propertyName.trim() }, { informationType: 'price', displayValue: propertyElement.pricePerMonth } ], 'value': `I would like to checkout ${propertyElement.propertyName.trim()}` } propertyResponse.push(respOBJ); responseObject.push({ type: "message", text: "Yes" }) // responseObject.push({ type: 'carousal', value: { data: propertyResponse } }) } else responseObject.push({ type: "message", text: "no" }) return responseObject; } async function firstTimeOrUpdatedResposne(conversationData) { let responseObject = []; // for first time slots answered given if (conversationData.firstTimeSlotCounter > 1) { conversationData.firstTimeSlotArray = resolveCamelCase(conversationData.firstTimeSlotArray); const manyFirstOrUpdateRequest = { 'type': 'first-many', 'intentName': conversationData.intentName } const manyFirstOrUpdateResponse = manyFirstOrUpdate(manyFirstOrUpdateRequest) responseObject.push({ type: manyFirstOrUpdateResponse[0].type, text: manyFirstOrUpdateResponse[0].text + ' ' + conversationData.firstTimeSlotArray.join(', ') }) } else if (conversationData.firstTimeSlotCounter === 1) { conversationData.firstTimeSlotArray = resolveCamelCase(conversationData.firstTimeSlotArray); const slotName = conversationData.firstTimeSlotArray[0] const manyFirstOrUpdateRequest = { 'type': 'first-one', 'intentName': conversationData.intentName } const manyFirstOrUpdateResponse = manyFirstOrUpdate(manyFirstOrUpdateRequest) responseObject.push({ type: manyFirstOrUpdateResponse[0].type, text: manyFirstOrUpdateResponse[0].text + ' ' + conversationData.firstTimeSlotArray.join() }) } // for updated slots answered given if (conversationData.updatedSlotCounter > 1) { conversationData.updatedSlotArray = resolveCamelCase(conversationData.updatedSlotArray); const manyFirstOrUpdateRequest = { 'type': 'updated-many', 'intentName': conversationData.intentName } const manyFirstOrUpdateResponse = manyFirstOrUpdate(manyFirstOrUpdateRequest) responseObject.push({ type: manyFirstOrUpdateResponse[0].type, text: manyFirstOrUpdateResponse[0].text + ' ' + conversationData.updatedSlotArray.join(', ') }) } else if (conversationData.updatedSlotCounter === 1) { const updatedRequestObject = inputErrorRequestStructure('Updated', conversationData.conversationId, conversationData.intentName, conversationData.updatedSlotArray.join()) let updateResponseObject = await responseLibrary(updatedRequestObject) updateResponseObject = await JSON.parse(updateResponseObject); responseObject = responseObject.concat(updateResponseObject) } return responseObject; } async function getParsedObjectfromApi(apiObj) { const response = await new Promise((resolve, reject) => { const reqBody = apiObj.requestBody; const reqHeader = apiObj.requestHeaders; const path = apiObj.requiredApiArray; const apiUrl = apiObj.apiUrl; request({ url: apiUrl, method: 'POST', json: true, body: reqBody, gzip: true, headers }, (error, response, body) => { if (error) { console.error(error) reject(error); } else { const pathSplit = path.split('.'); if (pathSplit.length === 1) { resolve(response.body[`${pathSplit[0]}`][0]) } else { resolve(response.body[`${pathSplit[0]}`][`${pathSplit[1]}`][0]); } } }); }); return response } function getValueFromPath(responseFromApi, path, keyValue) { const pathArray = path.split('.') let responseObjectFromApi = responseFromApi; for (const ele of pathArray) { responseObjectFromApi = responseObjectFromApi[ele] } const response = {}; response[keyValue] = responseObjectFromApi; return response } async function intentMap(intentName, fieldValues) { let intentMapObjectFromDb = intentMapData.filter(ele => ele.intentName === intentName) intentMapObjectFromDb = intentMapObjectFromDb[0] fieldValues = fieldValues.filter(ele => Object.keys(ele)[0] === intentMapObjectFromDb.entity.name) if (intentMapObjectFromDb.entity.isList) { fieldValues = fieldValues[0][intentMapObjectFromDb.entity.name].listValue.value } else { fieldValues = [fieldValues[0][intentMapObjectFromDb.entity.name].stringValue] } const responseFromApi = await getParsedObjectfromApi(intentMapObjectFromDb.apiDetails).then((res) => { return res }).catch((err) => { return err }); const fieldsWithPath = intentMapObjectFromDb.fieldsWithPath; const valueArray = []; let counter = 0; for (const ele of fieldsWithPath) { for (const keyValue in ele) { if (fieldValues.includes(keyValue)) { counter = counter + 1 valueArray.push(getValueFromPath(responseFromApi, ele[keyValue], keyValue)) } } } const resolvedToDisplayValueMap = intentMapObjectFromDb.resolvedToDisplayValueMap; let responseText; if (valueArray && valueArray.length) { let responseObjectFromDb = responseLibraryData.filter(ele => ele.KeyValueCount === counter && ele.Intent === intentName) responseObjectFromDb = responseObjectFromDb[0] responseText = responseObjectFromDb['Text Response'] let valueToBeReplace; for (const ele of valueArray) { // tslint:disable-next-line:forin for (const key in ele) { const resolveToValueObject = resolvedToDisplayValueMap.filter(ele1 => ele1.resolvedValue === key)[0] if (resolveToValueObject) valueToBeReplace = resolveToValueObject.displayValue; else valueToBeReplace = key responseText = responseText.replace(intentMapObjectFromDb.valueToReplaceInResponse, valueToBeReplace) responseText = responseText.replace('@value', ele[key]) } } } return [{ 'type': 'message', text: responseText }]; } function resolveCamelCase(requestArray) { let counter = -1; for (let ele of requestArray) { counter = counter + 1; switch (ele) { case 'sharingType': { ele = 'sharing type' requestArray[counter] = ele; break; } case 'phone-number': { ele = 'phone number' requestArray[counter] = ele; break; } case 'flatType': { ele = 'flat type' requestArray[counter] = ele; break; } case 'foodHabits': { ele = 'food habits' requestArray[counter] = ele; break; } case 'montlyRent': { ele = 'montly rent' requestArray[counter] = ele; break; } } } return requestArray; } async function getPropertyElement(propertyArray) { const getPropertyIdUri = apis.getPropertyByLocationLink; const getPropertyDetailsUri = apis.getRoomWiseDetailFromPropertyId let jsonData; const requestBody = { LocationLink: propertyArray } const property = await axios.post(getPropertyIdUri, requestBody, { headers }) .then((response) => { jsonData = response; return jsonData.data }) .catch((error) => { console.log(error) }) let propertyId; let propertyDetails; let req; if (property) { if (property.Data.Property && property.Data.Property.length) { propertyId = property.Data.Property[0].PropertyID const locationName = property.Data.Property[0].SubLocation const propertyName = property.Data.Property[0].PropertyName const imageName = property.Data.Property[0].ImageName const pricePerMonth = property.Data.Property[0].PricePerMonth req = { PropertyID: propertyId } propertyDetails = await axios.post(getPropertyDetailsUri, req, { headers }) .then((response) => { jsonData = response; return jsonData.data }) .catch((error) => { console.log(error) }) const propertyD = { location: locationName, propertyDetails, propertyName, imageName, pricePerMonth } return propertyD } } } async function getPropertyDetailFromId(propertyId) { const getPropertyDetailsUri = apis.getRoomWiseDetailFromPropertyId const req = { PropertyID: propertyId } // return await axios.post(getPropertyDetailsUri, req, { // headers // }) // .then((response) => { // return response.data // }) // .catch((error) => { // console.log(error) // }) let property = await new Promise((resolve, reject) => { request({ url: getPropertyDetailsUri, method: 'POST', json: true, body: req, gzip: true, headers }, (error, res, body) => { if (error) { console.error(error) reject(error); } resolve(res.body); }); }) return property } async function getPropertyDetailFromId1(propertyIdAndNameJson) { const getPropertyDetailsUri = apis.getRoomWiseDetailFromPropertyId let req; const response = {}; let property; // tslint:disable-next-line:forin for (const ele in propertyIdAndNameJson) { req = { PropertyID: ele } property = await new Promise((resolve, reject) => { request({ url: getPropertyDetailsUri, method: 'POST', json: true, body: req, gzip: true, headers }, (error, res, body) => { if (error) { console.error(error) reject(error); } resolve(res.body); }); }) if (property.Data && property.Data.length) { const propertyName = propertyIdAndNameJson[ele] response[propertyName] = property.Data } } return response; } function removeDuplicates(arr, prop) { const obj = {}; for (let i = 0, len = arr.length; i < len; i++) { if (!obj[arr[i][prop]]) obj[arr[i][prop]] = arr[i]; } const newArr = []; for (const key in obj) newArr.push(obj[key]); return newArr; } async function statisticsApiFunction(obj, intentName) { const res = await checkApiResult(obj, '', '') const propertyData = res.propertyData; let finalRes; let response = []; let properties = []; if (propertyData && propertyData.length) { let message; if (propertyData.length === 1) { message = `There is ${propertyData.length} property available for your given preferences` } else { message = `There are total ${propertyData.length} properties available for your givenF preferences` } finalRes = generatePropertyResponseObject(propertyData, [], [], message, 2, intentName, ''); response = finalRes.response; properties = finalRes.exactMatch; } else { response.push({ type: 'message', 'text': 'There are no properties for your given preferences!!!' }) } const resp = { property: properties, data: response } return resp; } async function checkApiResult(obj, key1, value1) { const apiUri = apis.searchPropertyDetails; const entityKeyValueList = []; let locationArray = []; const entityList = obj; if (key1) { const value = value1.listValue.value entityKeyValueList.push({ 'slot': key1, 'entityValues': value }) if (key1 === 'location') { locationArray = value1.listValue.value; } } if (entityList && entityList.length) { entityList.forEach((entity, index) => { const key = entity.slot let value2; if (key === key1) value2 = value1.listValue.value else value2 = entity.entityValues entityKeyValueList.push({ 'slot': key, 'entityValues': value2 }); if (key === 'location') { locationArray = value2; } }) } const locationaName = locationArray[0]; const requestObject = await getRequestStructure(entityKeyValueList, locationaName); // tslint:disable-next-line:no-console // console.log("requestObject", requestObject) // tslint:disable-next-line:no-console let message await fetchDataFromApi(requestObject, apiUri) .then((res) => { if (res && res.Data && res.Data.Property) { const exactProperty = res.Data.Property if (exactProperty.length) { message = { success: true, slot: key1, value: value1, data: '', propertyData: exactProperty } } else { const jsonobject = { "startTime": new Date().getTime(), "response": 'It seems like we don\'t have any properties with that filter , You can choose another value for the same ', "suggestionResponse": getSuggestion(key1), "endTime": new Date().getTime() } const finalObj = generateFinalResponseObject(jsonobject, key1, 'cityValue'); message = { success: false, slot: key1, value: value1, data: finalObj, propertyData: exactProperty } } } }); return message; } async function responseGenerator(obj) { // const workbook = XLSX.readFile(__dirname + '/Colive Training.xlsx'); // const sheetNameList = workbook.SheetNames; // const responseLibraryData = XLSX.utils.sheet_to_json(workbook.Sheets[sheetNameList[0]]); suggestion = db; const lastSlotAsked = obj.lastSlotAsked const startTime = new Date().getTime(); let response; const userId = obj.userID; const intentName = obj.data[1].intent; const entityList = obj.entityInfo; const entityKeyValueList = []; let cityValue; let locationArray; if (entityList && entityList.length) { entityList.forEach((entity, index) => { const key = Object.keys(entity)[0]; // tslint:disable-next-line:no-string-literal const value = entity[key].listValue['value']; entityKeyValueList.push({ 'slot': key, 'entityValues': value }); if (key === 'city') { cityValue = value; } else if (key === 'location') { locationArray = value; } }) } // tslint:disable-next-line:no-shadowed-variable let propertyList; let intentFilterData = []; // tslint:disable-next-line:no-console // console.log('request sent to ML', obj) const responseObjectFromML = await getResponseFromMlEndpoint(obj); if (responseObjectFromML && responseObjectFromML.data && responseObjectFromML.data.prediction && responseObjectFromML.data.prediction.length) { // tslint:disable-next-line:no-console // console.log('responseObjectFromML', responseObjectFromML.data.prediction); const Arr = []; const responseFromMlLibraryCount = responseObjectFromML.data.prediction.length; // responseObjectFromML.data.prediction[0].type = 'API'; // tslint:disable-next-line: prefer-for-of for (let i = 0; i < responseObjectFromML.data.prediction.length; i++) { if (responseObjectFromML.data.prediction[i].type === 'slot') { const slotName = responseObjectFromML.data.prediction[i].value; intentFilterData = responseLibraryData.filter( item => item.Intent === intentName && item['Response Type'] === "First" && item['Qualifiers/Slots/Filters'] === responseObjectFromML.data.prediction[i].value); if (intentFilterData && intentFilterData.length) { response = generateRandomResponse(intentFilterData, userId, intentName); const jsonobject = { "startTime": startTime, "response": response['Text Response'], "suggestionResponse": getSuggestion(response['Qualifiers/Slots/Filters']), "endTime": new Date().getTime() } const finalObj = generateFinalResponseObject(jsonobject, slotName, cityValue); Arr.push(finalObj) } } if (responseObjectFromML.data.prediction[i].type === 'API') { const endResponseArray = responseLibraryData.filter(item => item.Intent === intentName && item['Qualifiers/Slots/Filters'] === 'end'); endString = endResponseArray && endResponseArray.length ? endResponseArray[0] : {}; if (locationArray && locationArray.length > 1) { let locationName; let similarProperty = []; let exactProperty = []; let trendingProperty = []; for (locationName of locationArray) { const requestObject = await getRequestStructure(entityKeyValueList, locationName); // tslint:disable-next-line:no-console // console.log("requestObject", requestObject) // responseObjectFromML.data.prediction[i].value await fetchDataFromApi(requestObject, apis.searchPropertyDetails) .then((res) => { if (res && res.Data && res.Data.Property) { exactProperty = exactProperty.concat(res.Data.Property) similarProperty = similarProperty.concat(res.Data.SimilarProperties) trendingProperty = trendingProperty.concat(res.Data.TrendingProperties) } }); } exactProperty = removeDuplicates(exactProperty, 'PropertyName') similarProperty = removeDuplicates(similarProperty, 'PropertyName') trendingProperty = removeDuplicates(trendingProperty, 'PropertyName') propertyList = generatePropertyResponseObject(exactProperty, similarProperty, trendingProperty, endString['Text Response'], responseFromMlLibraryCount, intentName, lastSlotAsked); } else { const locationaName = locationArray ? locationArray[0] : ''; const requestObject = await getRequestStructure(entityKeyValueList, locationaName); // tslint:disable-next-line:no-console // console.log("requestObject", requestObject) // tslint:disable-next-line:no-console await fetchDataFromApi(requestObject, responseObjectFromML.data.prediction[i].value) .then((res) => { if (res && res.Data && res.Data.Property) { const similarProperty = res.Data.SimilarProperties const exactProperty = res.Data.Property const trendingProperty = res.Data.TrendingProperties propertyList = generatePropertyResponseObject(exactProperty, similarProperty, trendingProperty, endString['Text Response'], responseFromMlLibraryCount, intentName, lastSlotAsked); } }); } Arr.push(propertyList) } } return Arr; } } /** * Remove duplicates from an array of objects in javascript * @param arr - Array of objects * @param prop - Property of each object to compare * @returns {Array} */ async function validationCheck(key, values, contextArrayOfSlots, conversationId, intentName) { let response; let responseObject = []; if (key === 'phone-number') { const startingNumberValidation = /^[6789]/; const tenDigitNumber = /d{9}$/; const phoneno = /^[0]?[6789]\d{9}$/; const phoneNumber = values[0]; let resultFlag = false; let resultMessage; const resultType = 'message'; if (phoneNumber.match(phoneno)) { resultFlag = true; resultMessage = phoneNumber } else if (!phoneNumber.match(startingNumberValidation)) { const manyFirstOrUpdateRequest = { 'type': 'phone-starting-digits', 'intentName': intentName } const manyFirstOrUpdateResponse = manyFirstOrUpdate(manyFirstOrUpdateRequest) resultFlag = false; resultMessage = manyFirstOrUpdateResponse[0].text; } else if (!phoneNumber.match(tenDigitNumber)) { resultFlag = false; const manyFirstOrUpdateRequest = { 'type': 'phone-total-digits', 'intentName': intentName } const manyFirstOrUpdateResponse = manyFirstOrUpdate(manyFirstOrUpdateRequest) resultFlag = false; resultMessage = manyFirstOrUpdateResponse[0].text; } response = { success: resultFlag, message: [{ type: resultType, text: resultMessage }] } } else { switch (key) { case 'montlyRent': { response = { 'success': true, message: 'true' } break; } default: { let isValuePresentInSuggestionsStoreFlag = false; const SuggestionsStore = db.filter(ele => ele.filterName.toLowerCase() === key.toLowerCase()) // check is values are there or not if yes than check every element in values to be present in suggestion store or not if (values && values.length && SuggestionsStore && SuggestionsStore.length && !isValuePresentInSuggestionsStoreFlag) { values.forEach(element => { if (!isValuePresentInSuggestionsStoreFlag) { const SuggestionsStore1 = SuggestionsStore[0].filterValue; const isValuePresentInSuggestionsStore = SuggestionsStore1.filter(ele => ele.toLowerCase() === element.toLowerCase()) isValuePresentInSuggestionsStoreFlag = isValuePresentInSuggestionsStore && isValuePresentInSuggestionsStore.length ? true : false; } }); } // if no values are present in suggestion store then call respone library for data Input error if (isValuePresentInSuggestionsStoreFlag || !values.length) { response = { success: true, message: 'Validation Success' } } else { let finalObj; let errorResponseObject = []; // const workbook = XLSX.readFile(__dirname + '/Colive Training.xlsx'); // const sheetNameList = workbook.SheetNames; // const responseLibraryData = XLSX.utils.sheet_to_json(workbook.Sheets[sheetNameList[0]]); const intentFilterData = responseLibraryData.filter( item => item.Intent === intentName && item['Response Type'] === 'InputError' && item['Qualifiers/Slots/Filters'] === key); let responseObject = []; if (intentFilterData && intentFilterData.length) { const filterObject = intentFilterData[0]; const jsonobject = { "response": filterObject['Text Response'], "suggestionResponse": SuggestionsStore[0].filterValue } finalObj = generateFinalResponseObject(jsonobject, key, ''); } errorResponseObject = finalObj.response; const isErrorResponseObjectEmpty = !Object.keys(errorResponseObject).length; if (!isErrorResponseObjectEmpty) { responseObject = errorResponseObject } response = { 'success': false, message: responseObject } } } } } return await response; }; function inputErrorRequestStructure(typeOfRespose, userId, intent, slot) { const requestStruct = { // 'sessionID': uuid(), 'type': typeOfRespose, 'userID': userId, 'intent': intent, 'slot': slot } return requestStruct; } async function mlResponseGenerator(obj) { // const workbook = XLSX.readFile(__dirname + '/Colive Training.xlsx'); // const sheetNameList = workbook.SheetNames; // const responseLibraryData = XLSX.utils.sheet_to_json(workbook.Sheets[sheetNameList[0]]); // tslint:disable-next-line:no-console // console.log(responseLibraryData) // tslint:disable-next-line:no-console // console.log(obj, "here also") const intentName = obj.data[1].intent; const userId = obj.userID; const slot = obj.data[2].slots[1]; // tslint:disable-next-line:no-console // console.log(slot, "here"); let intentFilterData = []; intentFilterData = responseLibraryData.filter(item => item['Intent'] === intentName && item['Response Type'] === "First" && item['Qualifiers/Slots/Filters'] === slot); // tslint:disable-next-line:no-console // console.log(intentFilterData) const startTime = new Date().getTime(); let finalObj; let jsonObject; if (intentFilterData) { jsonObject = { "startTime": startTime, "response": "I guess you are looking for " + slot, "suggestionResponse": getSuggestion(slot), "endTime": new Date().getTime() } } else { jsonObject = { "startTime": startTime, "response": "I didn't get you!", "suggestionResponse": '', "endTime": new Date().getTime() } } finalObj = generateFinalResponseObject(jsonObject, slot, 'Banglore'); return await finalObj; } async function smallTalkResponseGenerator(obj) { // const workbook = XLSX.readFile(__dirname + '/Colive Training.xlsx'); // const sheetNameList = workbook.SheetNames; // const responseLibraryData = XLSX.utils.sheet_to_json(workbook.Sheets[sheetNameList[0]]); const intentName = obj.intentName; const isLoginUser = obj.userObject; let intentFilterData; let crmFilterData; intentFilterData = responseLibraryData.filter(item => item.Intent === intentName); if (isLoginUser) crmFilterData = intentFilterData.filter(item => item.Intent === intentName && item.isCrmResponse === true); if (crmFilterData && crmFilterData.length) intentFilterData = crmFilterData let responseObject = []; // = new DirectResponseObject(); // responseObject.type = 'text' // const re = /\<(.|\n)*?\>/g; // const result = str.match(re); // for (const element of result) { // str = str.replace(element, isLoginUser.CustomerName); // const elementWithBraces = element.substring(1, element.length - 1) // const entityTypeAndValue = elementWithBraces.split(':') // } let responseStr; if (intentFilterData && intentFilterData.length) { const filterObject = intentFilterData[0]; responseStr = filterObject['Text Response'] responseStr = isLoginUser ? responseStr.replace('', isLoginUser.CustomerName) : responseStr; const textResponseLength = responseStr.match(/[\s\S]{1,50}/g) || []; if (textResponseLength.length > 2) { const reply2 = { 'id': 'm01', 'text': responseStr } responseObject = [ { type: 'paragraph', value: reply2 }]; } else { responseObject = [ { type: 'message', text: responseStr } ] } if (filterObject.Suggestions) { responseObject.push(suggestionResponse(filterObject)); } } return responseObject; } function suggestionResponse(filterObject) { const suggestionArray = filterObject.Suggestions.split(","); const obj2 = new ResponseData(); obj2.type = "plainQuickReply"; const dataArray = []; suggestionArray.forEach((ele, index) => { const data2 = new ResponseSuggestionData(); data2.id = index + 1; data2.text = ele; data2.value = ele; dataArray.push(data2); }) obj2.value = { data: dataArray } let responseObject = {}; responseObject = obj2; return responseObject } function manyFirstOrUpdate(obj) { // const workbook = XLSX.readFile(__dirname + '/Colive Training.xlsx'); // const sheetNameList = workbook.SheetNames; // const responseLibraryData = XLSX.utils.sheet_to_json(workbook.Sheets[sheetNameList[0]]); const intentName = obj.intentName; const requestType = obj.type; const intentFilterData = responseLibraryData.filter(item => item['Intent'] === intentName && item['Qualifiers/Slots/Filters'] === requestType); const responseObject = []; const type = 'message' let textResponse; let voiceResponse; if (intentFilterData && intentFilterData.length) { const item = intentFilterData[Math.floor(Math.random() * intentFilterData.length)]; textResponse = item['Text Response'] voiceResponse = item['Voice Response'] } else { textResponse = "Noted your preferences for the following"; voiceResponse = "Noted your preferences for the following"; } let resObject = {}; if (requestType === 'customize-more') { resObject = { type: 'plainQuickReply', value: { 'id': 'm001', 'data': [ { 'id': '001', 'text': textResponse, 'value': textResponse }] } } } else { resObject = { // tslint:disable-next-line:object-literal-shorthand type: type, text: textResponse } } responseObject.push(resObject) return responseObject; } function responseFromResponseDb(obj) { const intentName = obj.intentName; const requestType = obj.type; const intentFilterData = responseLibraryData.filter(item => item.Intent === intentName && item['Qualifiers/Slots/Filters'] === requestType); let textResponse; let voiceResponse; let filterObject; let suggestionObject; let resObject = []; if (intentFilterData && intentFilterData.length) { filterObject = intentFilterData[Math.floor(Math.random() * intentFilterData.length)]; textResponse = filterObject['Text Response'] voiceResponse = filterObject['Voice Response'] } else { textResponse = "Response not found in response library"; voiceResponse = "Response not found in response library"; } if (filterObject) { const textResponseLength = textResponse.match(/[\s\S]{1,75}/g) || []; if (textResponseLength.length > 2) { const reply2 = { 'id': 'm01', 'text': textResponse } resObject = [ { type: 'paragraph', value: reply2 }]; } else { resObject = [ { type: 'message', text: textResponse } ] } if (filterObject.Suggestions) { suggestionObject = suggestionResponse(filterObject); resObject.push(suggestionObject) } } return resObject; } function inputErrorResponseGenerator(obj) { // const workbook = XLSX.readFile(__dirname + '/Colive Training.xlsx'); // const sheetNameList = workbook.SheetNames; // const responseLibraryData = XLSX.utils.sheet_to_json(workbook.Sheets[sheetNameList[0]]); const intentName = obj.intent; const slot = obj.slot; const intentFilterData = responseLibraryData.filter( item => item['Intent'] === intentName && item['Response Type'] === obj.type && item['Qualifiers/Slots/Filters'] === slot); let responseObject = []; if (intentFilterData && intentFilterData.length) { const filterObject = intentFilterData[0]; responseObject = [ { type: 'message', text: filterObject['Text Response'] } ] let suggestionArray = []; if (filterObject.Suggestions) { suggestionArray = filterObject.Suggestions.split(","); if (suggestionArray.length === 1) { if (suggestionArray[0] === 'apiCall') { suggestionArray = getSuggestion(slot); } } // responseObject.suggestions = []; const obj2 = new ResponseData(); obj2.type = "plainQuickReply"; const dataArray = [] suggestionArray.forEach((ele, index) => { const data2 = new ResponseSuggestionData(); data2.id = index + 1; data2.text = ele; data2.value = ele; dataArray.push(data2); }) obj2.value = { data: dataArray } responseObject.push(obj2) } } return responseObject; } function generateRandomResponse(intentFilterData, userId, intentName) { const randomNumber = randomGenerator(intentFilterData.length) const response = intentFilterData[randomNumber]; return response } async function fetchDataFromApi(requestStructure, apiUrl) { let property = new Prop(); property = await new Promise((resolve, reject) => { request({ url: apiUrl, method: 'POST', json: true, body: requestStructure, gzip: true, headers }, (error, res, body) => { if (error) { console.error(error) reject(error); } resolve(res.body); }); }) return property; } async function getRequestStructure(entityKeyValueList, locationName) { // console.log(obj); let cityFlag = false; let locationFlag = false; let rentFlag = false; const locationArray = []; let cityArray = []; // const locationData = locationName ? getMyLoctaion(locationName) : '' // console.log(locationData) let requestStructure = new RequestStructureClass(); requestStructure = { 'AmenityIds': '', 'Date': '', 'Distance': '', 'FoodHabitsIds': '', 'FurnishStatusIds': '', 'HobbyIds': '', 'LanguagesIds': '', 'Latitude': '', 'LocationName': '', 'Longitude': '', 'MaxValue': '', 'MinValue': '', 'OfferId': '', 'RoomClassIds': '', 'RoomShareCategory': '', 'RoomSubTypeIds': '', 'RoomTypeIds': '' } if (entityKeyValueList && entityKeyValueList.length) { entityKeyValueList.forEach((ele, index) => { switch (ele.slot) { case 'sharingType': { const ids = []; let id; // console.log("in sharing type") ele.entityValues.forEach(element => { // console.log("wa",ele) const element1 = filters.RoomType.filter(ele => ele.Name.toLowerCase() === element.toLowerCase()) if (element1 && element1.length) { id = element1[0] ? element1[0].Id : ""; ids.push(id) } }); if (ids.length) { id = ids.join(); requestStructure.RoomTypeIds = id; } break; } case 'montlyRent': { let maxValue; let minValue; let rents = []; rentFlag = true; if (ele.entityValues && ele.entityValues.length) { rents = ele.entityValues[0].split("|") if (rents.length === 1) { maxValue = rents[0]; minValue = 2500; } else { const max = rents.reduce((a, b) => { return Math.max(a, b); }); const min = rents.reduce((a, b) => { return Math.min(a, b); }); maxValue = max ? max : 40000; minValue = min ? min : 4000; } } requestStructure.MaxValue = maxValue; requestStructure.MinValue = minValue; break; } case 'amenities': { const ids = []; let id; ele.entityValues.forEach(element => { const element1 = filters.Amenities.filter(ele => ele.Name.toLowerCase() === element.toLowerCase()) if (element1 && element1.length) { id = element1[0] ? element1[0].Id : ""; ids.push(id) } }); id = ids.join(); requestStructure.AmenityIds = id; break; } case 'languages': { const ids = []; let id; ele.entityValues.forEach(element => { const element1 = filters.Additional.Languages.filter(ele => ele.Name.toLowerCase() === element.toLowerCase()) if (element1 && element1.length) { id = element1[0] ? element1[0].Id : ""; ids.push(id) } }); id = ids.join(); requestStructure.LanguagesIds = id; break; } case 'hobbies': { const ids = []; let id; ele.entityValues.forEach(element => { const element1 = filters.Additional.Hobbies.filter(ele => ele.Name.toLowerCase() === element.toLowerCase()) if (element1 && element1.length) { id = element1[0] ? element1[0].Id : ""; ids.push(id) } }); id = ids.join(); requestStructure.HobbyIds = id; break; } case 'foodHabits': { const ids = []; let id; ele.entityValues.forEach(element => { const element1 = filters.Additional.FoodHabits.filter(ele => ele.Name.toLowerCase() === element.toLowerCase()) if (element1 && element1.length) { id = element1[0] ? element1[0].Id : ""; ids.push(id) } }); id = ids.join(); requestStructure.FoodHabitsIds = id; break; } case 'city': { cityFlag = true; cityArray = ele.entityValues; break; } case 'location': { locationFlag = true; locationArray.push(locationName); break; } } }); if (cityFlag && locationFlag) { const locationId = locationArray.join("/"); const cityId = cityArray.join("/"); requestStructure.Latitude = locationArray[0].latitude requestStructure.Longitude = locationArray[0].longitude // requestStructure.LocationName = cityId + '/' + locationId; requestStructure.LocationName = ''; } else if (cityFlag) { const cityId = cityArray.join("/") requestStructure.LocationName = cityId; // requestStructure.LocationName=''; } else if (locationFlag) { const cityId = 'Bangalore'; const locationId = locationArray.join("/"); requestStructure.Latitude = locationArray[0].latitude requestStructure.Longitude = locationArray[0].longitude // requestStructure.LocationName = cityId + '/' + locationId; requestStructure.LocationName = ''; } if (!rentFlag) { requestStructure.MaxValue = 40000; requestStructure.MinValue = 3000; } } return requestStructure; } async function getResponseFromMlEndpoint(obj) { const apiUrl = apis.getFilterSuggestionsFromMl let responseObject; return await axios.post(apiUrl, obj) .then((response) => { responseObject = response; return responseObject; }) .catch((error) => { console.log(error) }) } function generateFinalResponseObject(jsonObject, slotName, cityValue) { // console.log(jsonObject, slotName) let responseType; const response = []; responseType = "text"; const textResponse = { type: 'message', text: jsonObject.response } response.push(textResponse); if (jsonObject.suggestionResponse) { const suggestionsList = jsonObject.suggestionResponse; if (suggestionsList && suggestionsList.length) { switch (true) { case slotName === 'montlyRent': { const dataArray = []; suggestionsList.forEach((ele, index) => { const data2 = new SliderResponseSuggestionData(); data2.id = index + 1; data2.rangeTitle = ele; data2.rangeValue = ele; dataArray.push(data2); }) responseType = "slider"; const values = { id: 's01', sliderTitle: 'Budget Selector', sliderMaxRange: '>40000', sliderMinRange: 5000, data: dataArray } const sliderObject = { type: responseType, value: values } response.push(sliderObject); break; } case slotName === 'sharingType' || slotName === 'flatType' || slotName === 'gender': { // tslint:disable-next-line:no-shadowed-variable const obj2 = new ResponseData(); obj2.type = "iconQuickReply"; responseType = "iconQuickReply"; // obj2.data = []; const dataArray = []; suggestionsList.forEach((ele, index) => { const data2 = new IconResponseSuggestionData(); const iconUrlObjectArray = iconUrl.filter(element => element.text === ele) data2.id = index + 1; data2.text = ele; data2.value = ele; data2.iconUrl = iconUrlObjectArray && iconUrlObjectArray.length ? iconUrlObjectArray[0].iconUrl : '' dataArray.push(data2); }) obj2.value = { data: dataArray } response.push(obj2); break; } case slotName === 'location': { const cityArray = []; if (cityValue && cityValue.length) { const cityValuesArray = locations.filter(ele => ele.Location === cityValue[0]) if (cityValuesArray && cityValuesArray.length) { cityValuesArray[0].Level2.forEach(element => { cityArray.push(element.Location) element.Level0.forEach(element2 => { cityArray.push(element2.Location) }); }); } } else { locations.forEach(element => { element.Level2.forEach(element1 => { cityArray.push(element1.Location) element1.Level0.forEach(element2 => { cityArray.push(element2.Location) }); }); }); } // tslint:disable-next-line:no-shadowed-variable const obj2 = new ResponseData(); obj2.type = "plainQuickReply"; responseType = "plainQuickReply"; const dataArray = [] cityArray.forEach((ele, index) => { const data2 = new ResponseSuggestionData(); data2.id = index + 1; data2.text = ele; data2.value = ele; dataArray.push(data2); }) obj2.value = { data: dataArray } response.push(obj2); break; } default: { const obj2 = new ResponseData(); responseType = "plainQuickReply"; obj2.type = "plainQuickReply"; const dataArray = [] suggestionsList.forEach((ele, index) => { const data2 = new ResponseSuggestionData(); data2.id = index + 1; data2.text = ele; data2.value = ele; dataArray.push(data2); }) obj2.value = { data: dataArray } response.push(obj2); } } } } const obj = { "slot": slotName, "responseType": responseType, "response": response } return obj; } function getSuggestion(slotName) { let suggestionResponse = []; if (slotName === 'location') { locations.forEach(element => { element.Level2.forEach(element1 => { suggestionResponse.push(element1.Location) element1.Level0.forEach(element2 => { suggestionResponse.push(element2.Location) }); }); });; } else if (db.length) { const suggestionList = db.filter(item => item.filterName === slotName); // console.log('suggestion', suggestionList) if (suggestionList && suggestionList.length) { suggestionResponse = suggestionList[0].filterValue; } } return suggestionResponse; } function randomGenerator(limit) { return Math.floor(Math.random() * Math.floor(limit)) } function generatePropertyResponseObject(exactProperty, similarProperty, trendingProperty, endStringResponse, responseFromMlLibraryCount, intentName, lastSlotAsked) { const propertyResponse = new ResponseProperty(); const propertyData = []; propertyResponse.id = "1"; propertyResponse.responseType = "carousal"; let propertiesToShow; let propertiesToShowCount; let toShowSimilarPropertyFlag = true; let toShowTrendingPropertyFlag = true; const extraButtonsData = []; if (exactProperty && exactProperty.length) { propertiesToShow = exactProperty propertiesToShowCount = exactProperty.length } else if (similarProperty && similarProperty.length) { propertiesToShow = similarProperty propertiesToShowCount = similarProperty.length similarProperty = []; toShowSimilarPropertyFlag = false; toShowTrendingPropertyFlag = true; } else if (trendingProperty && trendingProperty.length) { propertiesToShow = trendingProperty propertiesToShowCount = trendingProperty.length trendingProperty = []; toShowSimilarPropertyFlag = false; toShowTrendingPropertyFlag = false; } if (toShowSimilarPropertyFlag && toShowTrendingPropertyFlag) { if (!similarProperty.length) { toShowSimilarPropertyFlag = false; } if (!trendingProperty.length) { toShowTrendingPropertyFlag = false; } } else if (toShowTrendingPropertyFlag) { if (!trendingProperty.length) { toShowTrendingPropertyFlag = false; } } propertyResponse.exactMatch = []; propertyResponse.trendingProperty = []; propertyResponse.similarProperty = []; // tslint:disable-next-line:prefer-for-of if (toShowSimilarPropertyFlag) { extraButtonsData.push({ 'id': '001', 'text': 'Similar Property', 'value': 'Similar Property' }) for (let i = 0; i < similarProperty.length; i++) { const respOBJ = { title: similarProperty[i].PropertyName.trim(), imageURL: similarProperty[i].ImageName, description: [ { informationType: 'location', displayValue: similarProperty[i].LocationName }, { informationType: 'price', displayValue: similarProperty[i].Price } ], 'value': `I would like to view more on property ${similarProperty[i].PropertyName}` } propertyResponse.similarProperty.push(respOBJ); } } if (toShowTrendingPropertyFlag) { extraButtonsData.push({ 'id': '001', 'text': 'Trending Property', 'value': 'Trending Property' }) // tslint:disable-next-line:prefer-for-of for (let i = 0; i < trendingProperty.length; i++) { const respOBJ = { title: trendingProperty[i].PropertyName.trim(), imageURL: trendingProperty[i].ImageName, description: [ { informationType: 'location', displayValue: trendingProperty[i].LocationName }, { informationType: 'price', displayValue: trendingProperty[i].Price } ], 'value': `I would like to view more on property ${trendingProperty[i].PropertyName}` } propertyResponse.trendingProperty.push(respOBJ); } } let responseObject = []; if (propertiesToShow && propertiesToShow.length) { if (propertiesToShow.length > 1) propertyResponse.carousalTitle = `Here are some ${propertiesToShowCount} cool properties....` else propertyResponse.carousalTitle = `Here is ${propertiesToShowCount} cool property that matches your preferences....` responseObject.push({ type: 'message', text: propertyResponse.carousalTitle }) propertiesToShow.forEach((property, index) => { const propertyObj = new ResponsePropertyData(); propertyObj.id = index + 1; propertyObj.title = property.PropertyName.trim(); propertyObj.subtitle = property.PropertyName; propertyObj.imageURL = property.ImageName; propertyObj.value = `I would like to view more on property ${property.PropertyName}`; const obj = []; obj.push({ informationType: "price", displayValue: `${property.Price}` }) obj.push({ informationType: "location", displayValue: `${property.LocationName}` }) propertyObj.description = obj; propertyData.push(propertyObj); }); responseObject.push({ type: 'carousal', value: { data: propertyData } }) if (extraButtonsData && extraButtonsData.length) { responseObject.push({ type: 'plainQuickReply', value: { data: extraButtonsData, id: 'm001', } }) } if (responseFromMlLibraryCount === 1) { const manyFirstOrUpdateRequest = { 'type': 'show-property-end-message', 'intentName': intentName } const manyFirstOrUpdateResponse = manyFirstOrUpdate(manyFirstOrUpdateRequest) responseObject = responseObject.concat(manyFirstOrUpdateResponse) } propertyResponse.exactMatch = propertyData; } else { const jsonobject = { "startTime": new Date().getTime(), "response": 'It seems like we don\'t have any properties with that option, you can choose another value for the same ', "suggestionResponse": getSuggestion(lastSlotAsked), "endTime": new Date().getTime() } const finalObj = generateFinalResponseObject(jsonobject, lastSlotAsked, 'cityValue'); responseObject = finalObj.response; // responseObject.push( // { type: 'message', text: 'I couldn\'t find an exact match....' }, // { type: 'message', text: 'Change your preferences to get better result !' }) } propertyResponse.response = responseObject; return propertyResponse; } function getMyLoctaion(location) { const Arr = []; for (const data of locationLatLong) { if (data.Location.toLowerCase() === location.toLowerCase()) { Arr.push(data) } } return Arr; } class RequestStructureClass { AmenityIds; Date; Distance; FoodHabitsIds; FurnishStatusIds; HobbyIds; LanguagesIds; Latitude; LocationName; Longitude; MaxValue; MinValue; OfferId; RoomClassIds; RoomShareCategory; RoomSubTypeIds; RoomTypeIds; } // tslint:disable-next-line:max-classes-per-file class ResponseProperty { id; responseType; exactMatchCount; similarPropertyCount; trendingPropertyCount; carousalTitle; exactMatch; similarProperty; trendingProperty; response; }; // tslint:disable-next-line:max-classes-per-file class ResponsePropertyData { id; title; subtitle; imageURL; value; description; }; // tslint:disable-next-line:max-classes-per-file class ResponsePropertyDescData { informationType; displayValue; }; // tslint:disable-next-line:max-classes-per-file class ResponseData { type; value; }; // tslint:disable-next-line:max-classes-per-file // tslint:disable-next-line:max-classes-per-file class ResponseTextData { textResponse; voiceResponse; }; // tslint:disable-next-line:max-classes-per-file class ResponseSuggestionData { id; text; value; }; // tslint:disable-next-line:max-classes-per-file class SliderResponseSuggestionData { id; rangeTitle; rangeValue; }; // tslint:disable-next-line:max-classes-per-file class IconResponseSuggestionData { id; text; iconUrl; value; }; // tslint:disable-next-line:max-classes-per-file class DirectResponseObject { type; textResponse; voiceResponse; suggestions; } // tslint:disable-next-line:max-classes-per-file class Prop { Data; } module.exports = { intentMap, responseFromResponseDb, responseLibrary, firstTimeOrUpdatedResposne, validationCheck, manyFirstOrUpdate, getPropertyDetail, checkApiResult, getPropertyDetailFromId, statisticsApiFunction, getPropertyDetailFromId1 }