Name

sn_ci_analytics.CAPayloadBuilder

Description

No description available

Script

var CAPayloadBuilder = Class.create();

CAPayloadBuilder.prototype = {
  INTENT_AUTO_SELECTED: 'Auto-Select',
  INTENT_CORRECT: 'Correct',
  INTENT_INCORRECT: 'Incorrect',
  INTENT_UNSUPPORT: 'Unsupport',
  NO_DEFLECTION: '10',

  initialize: function() {
      this.logger = CIAnalyticsLogger.getLogger("CAPayloadBuilder");
      this.util = new CAUtil();
  },

  getConversation: function() {
      return this.conversation;
  },

  getSharedData: function() {
      return this.sharedData || {};
  },

  _setCommonConversationData: function(caConversation, interactionGr) {
      this.conversationStartTimeInMillis = new GlideDateTime(caConversation.conversation.sys_created_on).getNumericValue();
      this.conversationEndState = this.conversation['DynamicProperties']['End State'];
      this.conversationChannel = this._valueCheck(caConversation.conversation.device_type.getDisplayValue());
      this.liveAgentTranferFlag = !gs.nil(caConversation.conversation.live_agent_transfer_time);
      this.conversationType = this._valueCheck(caConversation.conversation.conversation_type);
      this.sharedData['conversation_end_state'] = this.conversationEndState;
      this.sharedData['conversation_startTime_mills'] = this.conversationStartTimeInMillis;
      this._logDebug('Conversation Duration: {2}', [this.conversationStartTimeInMillis]);
  },

  _setCommonTopicData: function(topicGr) {
      this.topicName = this._valueCheck(topicGr.topic_type.sys_id);
      this.topicDescription = this._valueCheck(topicGr.topic_type.name);
  },

  _getConversationDuration: function(caConversation) {
      if (!this.conversation['Duration']) {
          var startTime = new GlideDateTime(caConversation.conversation.sys_created_on);
          var completedTime = caConversation.conversation.conversation_completed ? new GlideDateTime(caConversation.conversation.conversation_completed) : new GlideDateTime(caConversation.conversation.sys_updated_on);
          return GlideDateTime.subtract(startTime, completedTime).getNumericValue();
      }

      return this.conversation['Duration'];
  },

  _getLAAndVADuration: function(caConversation, interactionGr) {
      var vaTime = 0;
      var laTime = 0;
      var live_agent_transfer_time = caConversation.conversation.live_agent_transfer_time;
      var startTime = new GlideDateTime(caConversation.conversation.sys_created_on);
      var completedTime = caConversation.conversation.conversation_completed ? new GlideDateTime(caConversation.conversation.conversation_completed) : new GlideDateTime(caConversation.conversation.sys_updated_on);
      var duration = GlideDateTime.subtract(startTime, completedTime).getNumericValue();
      if (live_agent_transfer_time && live_agent_transfer_time != '') {
          live_agent_transfer_time = new GlideDateTime(caConversation.conversation.live_agent_transfer_time);
          vaTime = GlideDateTime.subtract(startTime, live_agent_transfer_time).getNumericValue();
          laTime = GlideDateTime.subtract(live_agent_transfer_time, completedTime).getNumericValue();
      } else {
          vaTime = duration;
          laTime = 0;
      }
      if (!this.conversation['DynamicProperties']['VA Chat Duration'] || '' == this.conversation['DynamicProperties']['VA Chat Duration']) {
          this.conversation['DynamicProperties']['VA Chat Duration'] = vaTime;
      }
      if (!this.conversation['DynamicProperties']['Live Agent Chat Duration'] || '' == this.conversation['DynamicProperties']['Live Agent Chat Duration']) {
          this.conversation['DynamicProperties']['Live Agent Chat Duration'] = laTime;
      }
  },

  _setLAAbandonedDurationFromUser: function(caConversation, interactionGr) {
      var state = interactionGr.getValue('state');
      var reason = interactionGr.getValue('state_reason');
      var isLAChat = interactionGr.getValue('agent_chat');
      if (state == 'closed_abandoned' && reason == 'left_before_engagement' && isLAChat) {
          var task = this.queryConversationTask(caConversation.getValue('conversation'));
          while (task.next()) {
              var vaTransferTime;
              var closingTime;
              if (task.topic_type.name.includes('Closing Conversation')) {
                  closingTime = new GlideDateTime(task.sys_created_on).getNumericValue();
              }
              if (task.topic_type.name.includes('Live Agent Support')) {
                  vaTransferTime = new GlideDateTime(task.sys_created_on).getNumericValue();
              }
              if (vaTransferTime && closingTime) {
                  var userWaitingTime = closingTime - vaTransferTime;
              }
              this.conversation['DynamicProperties']['User Waiting Duration'] = userWaitingTime;
          }
      }
  },

  queryConversationTask: function(conversationId) {
      var conversationTask = new GlideRecord('sys_cs_conversation_task');
      conversationTask.addQuery('conversation', conversationId);
      conversationTask.addQuery('topic_type.is_system_topic', false);
      conversationTask.orderBy('sys_created_on');
      conversationTask.query();
      return conversationTask;
  },

  _setLastVisitedNodeName: function(nodeName) {
      this.topicEvent['Properties']['Last Visited Node Name'] = this._valueCheck(nodeName);
  },

  _setLastNodeVisited: function(node) {
      this.topicEvent['Properties']['Last Node Visited'] = node['type'] == 'TerminateGoal';
  },

  _getConversationType: function(caConversation, interactionGr) {
      var live_agent_transfer_time = caConversation.conversation.live_agent_transfer_time;
      var virtual_agent = interactionGr.getValue('virtual_agent');
      var liveAgentTransferTimeEmpty = gs.nil(live_agent_transfer_time);
      if (liveAgentTransferTimeEmpty && virtual_agent == true)
          return 'VA Only';
      else if (!liveAgentTransferTimeEmpty && virtual_agent == true)
          return 'VA to LA';
      else if (!liveAgentTransferTimeEmpty && virtual_agent == false)
          return 'LA Only';
      else
          return 'Never Engaged';
  },



  initConversation: function(conversationId) {
      this.conversation = {
          'DynamicProperties': {},
          'Screens': [],
          'Events': [],
          'User': {}
      };
      this.sharedData = {};
      this.predictions = {
          CORRECT: [],
          INCORRECT: [],
          AUTOSELECTED: [],
          UNSUPPORTED: [],
      };
      this.intents = [];
      this.conversationId = conversationId;
  },

  buildConversation: function(caConversation, interactionGr) {
      this.conversation['Id'] = this._valueCheck(caConversation.conversation.sys_id);
      this._setCommonConversationData(caConversation, interactionGr);
      var gd = new GlideDate();
      gd.setValue(caConversation.conversation.sys_created_on.toString());
      this.conversation['StartTime'] = gd.getByFormat("yyyy-MM-dd'T'HH:mm:ss.SS").toString();
      this._logDebug('Conversation start time: {2}, Converted start time: {3}', [caConversation.conversation.sys_created_on.toString(), this.conversation['StartTime']]);
      this.conversation['Locale'] = this._valueCheck(interactionGr.getValue('user_language'));
      this.conversation['Duration'] = this._getConversationDuration(caConversation);
      this._getLAAndVADuration(caConversation, interactionGr);
      this._setLAAbandonedDurationFromUser(caConversation, interactionGr);
      this.conversation['DynamicProperties']['Channel Name'] = this.conversationChannel;
      this.conversation['DynamicProperties']['Provider Name'] = this._valueCheck(
          interactionGr.provider_application && interactionGr.provider_application.provider && interactionGr.provider_application.provider.name ?
          interactionGr.provider_application.provider.name :
          ''
      );
      this.conversation['DynamicProperties']['Provider Identifier'] = this._valueCheck(
          interactionGr.provider_application && interactionGr.provider_application.name ?
          interactionGr.provider_application.name :
          ''
      );
      this.conversation['DynamicProperties']['Conversation Type'] = this._getConversationType(caConversation, interactionGr);
      this.conversation['DynamicProperties']['Initiated By'] = this.conversationType;
      this.conversation['DynamicProperties']['Language Translated'] = interactionGr.translated == true;
      this.conversation['DynamicProperties']['Agent Language Translated'] = (interactionGr.translated == true) && (interactionGr.agent_chat == true);
      this.conversation['DynamicProperties']['Multiple Predictions'] = 0;
      this.conversation['DynamicProperties']['Interaction Type'] = interactionGr.getValue('type');
      this.conversation['DynamicProperties']['Interaction Subtype'] = interactionGr.getValue('subtype');
      this.conversation['DynamicProperties']['Deflections'] = 0;
      this.conversation['DynamicProperties']['AI Search'] = false;
      this.conversation['DynamicProperties']['KB Shared'] = 0;
      this.updateInterfaceInformation(interactionGr);
  },

  updateInterfaceInformation: function(interactionGr) {
      var type = interactionGr.getValue('type').toLowerCase();
      this.conversation['DynamicProperties']['Interface'] = type.charAt(0).toUpperCase() + type.slice(1);
      if (interactionGr.getValue('subtype') === 'mweb' && interactionGr.getValue('type') === 'chat') {
          var context = new GlideRecord('interaction_context');
          context.addQuery('interaction', interactionGr.getUniqueValue());
          context.addQuery('name', 'liveagent_portal');
          context.query();
          if (context.next()) {
              this.conversation['DynamicProperties']['Interface-Type'] = 'Service Portal';
              this.conversation['DynamicProperties']['Interface-Name'] = context.getValue('value');
          } else {
              this.conversation['DynamicProperties']['Interface-Type'] = 'Unknown';
          }
      } else {
          this.conversation['DynamicProperties']['Interface-Type'] = 'Chat Integrations';
          this.conversation['DynamicProperties']['Interface-Name'] = interactionGr.getValue('subtype');
      }
  },

  updateMutiplePredictionsCount: function() {
      this.conversation['DynamicProperties']['Multiple Predictions']++;
  },

  setAISearchInvokedStatus: function() {
      this.conversation['DynamicProperties']['AI Search'] = true;
  },

  updateDeflectionCount: function(taskIds) {
      var gr = new GlideAggregate('ssa_deflection_metric');
      gr.addQuery('content_table', 'sys_cs_conversation_task');
      gr.addQuery('content_id', 'IN', taskIds);
      gr.addQuery('type', '!=', this.NO_DEFLECTION);
      gr.addAggregate('COUNT');
      gr.query();
      gr.next();
      var count = gr.getAggregate('COUNT');
      this.conversation['DynamicProperties']['Deflections'] = count;
  },

  updateSharedKBCount: function(eventName) {
      this.conversation['DynamicProperties']['KB Shared']++;
  },

  // Users

  addUser: function(caConversation) {
      this._buildUser(caConversation);
      this.conversation['User'] = this.user;
  },

  _buildUser: function(caConversation) {
      this.user = {
          'Properties': {}
      };

      var uniqueUser = null;
      var consumer = caConversation.conversation.consumer;
      var consumerName = !gs.nil(consumer) ? consumer.name : null;
      var user = !gs.nil(consumer) ? consumer.user_id : null;

      if (!gs.nil(consumerName) && consumerName != 'Guest' && !gs.nil(user)) {
          //check if it references a user in sys_user
          var userGr = new GlideRecord('sys_user');
          userGr.addQuery('sys_id', user);
          userGr.query();
          uniqueUser = userGr.next() ? ('consumer_user_' + user) : null;

      } else {

          //if consumer is not populated for a conversation, rely on consumerAccount
          var consumerAccount = caConversation.conversation.consumer_account;

          //if Guest or if there is no mapping, then it'll be guest user
          if (consumer == 'Guest' || gs.nil(consumerAccount) || gs.nil(consumerAccount.channel_user_profile)) {
              uniqueUser = null;
          } else {
              //if channel user profile exists, use user_document/channel_user_id
              var channelUserProfile = consumerAccount.channel_user_profile;

              if (!gs.nil(channelUserProfile)) {

                  //guest won't be linked userTable
                  //de-duping for guest users is determined based on channel_user_id
                  if (gs.nil(channelUserProfile.user_table)) {
                      uniqueUser = 'channel_user_' + channelUserProfile.channel_user_id;
                  }

                  //for non-guest users
                  //de-duping for non-guest users is determined based on user_document
                  else {
                      uniqueUser = 'user_document_' + channelUserProfile.user_document;
                  }
              }
          }
      }
      this.user['UserId'] = (uniqueUser != null) ? this._getUserHash(uniqueUser) : null;
      this.user['Properties']['Domain Id'] = this._valueCheck(caConversation.conversation.sys_domain);
  },


  _getUserHash: function(user) {
      var userId = this._valueCheck(new GlideDigest().getSHA256Hex(user));
      return userId ? userId.toLowerCase() : null;
  },

  // Screens

  addTopicScreen: function(taskGr) {
      this._buildTopicScreen(taskGr);
      this.conversation['Screens'].push(this.topicScreen);
  },

  _buildTopicScreen: function(taskGr) {
      this._setCommonTopicData(taskGr);
      this.topicScreen = {};
      this.topicScreen['Name'] = this.topicName;
      this.topicScreen['Description'] = this.topicDescription;
      this.topicScreen['Type'] = 'Topic';
      this.topicScreen['StartTime'] = this._getDuration(taskGr.sys_created_on);
  },

  _addTopicNodeScreen: function(topicNode) {
      this._buildTopicNodeScreen(topicNode);
      this.conversation['Screens'].push(this.topicNodeScreen);
      this._setLastVisitedNodeName(topicNode.name);
      this._setLastNodeVisited(topicNode);
  },

  _buildTopicNodeScreen: function(topicNode) {
      this.topicNodeScreen = {};
      this.topicNodeScreen['Name'] = this.topicName + '/' + topicNode.id;
      this.topicNodeScreen['Description'] = topicNode.name;
      this.topicNodeScreen['Type'] = 'TopicNode';
      this.topicNodeScreen['StartTime'] = this._getTopicNodeDuration(topicNode);
  },

  addTopic: function(taskGr) {
      this.addTopicScreen(taskGr);
      this.addTopicEvent(taskGr);
  },

  addTopicNodes: function(taskGr) {
      var taskContext = taskGr.getValue("context").substring(16);
      var taskContextXml = new global.VAGlobalUtil().getDecompressedValue(taskContext);
      this._logDebug('Conversation taskId: {2}, Context xml: {3}', [taskGr.getValue('sys_id'), taskContextXml]);
      // Parsing the xml
      var nodeListIndex = taskContextXml.indexOf('NodeDetailsList');
      var nodes = [];
      if (nodeListIndex > -1) {
          var nodeListXml = taskContextXml.substring(nodeListIndex);
          var valueStart = nodeListXml.indexOf('<value>');
          var valueEnd = nodeListXml.indexOf('</value>');
          var nodesCsv = nodeListXml.substring(valueStart + 7, valueEnd);
          nodesCsv = nodesCsv.replace(new RegExp('&#xd;', 'g'), '\r');
          var nodeInfo = nodesCsv.trim().split(new RegExp('\\r?\\n', 'g'));
          var headers = nodeInfo[0].split(',');
          if (this.logger.isDebugEnabled())
              this._logDebug('Parsed nodes: {2}', new global.JSON().encode(nodeInfo));
          for (var i = 1; i < nodeInfo.length; i++) {
              var nodeObj = {};
              var values = nodeInfo[i].split(',');
              for (j = 0; j < headers.length; j++)
                  nodeObj[headers[j]] = values[j];
              nodes.push(nodeObj);
          }
      }
      for (var k = 0; k < nodes.length; k++) {
          var node = nodes[k];
          this._addTopicNodeEvent(node, taskGr);
          this._addTopicNodeScreen(node);
      }
      this._recordTopicCompletionMetrics(taskGr);
  },

  /*
   * Method to collect topic completion information using GCF.
   */
  _recordTopicCompletionMetrics: function(taskGr) {
      try {
          var sm = new GCFSampleMap();
          sm.put('topic', taskGr.topic_type.name);
          sm.put('device_type', taskGr.conversation.device_type);
          sm.put('completed', this.topicEvent['Properties']['Last Node Visited']);
          var topicGr = new GlideRecord('sys_cb_topic');
          if (topicGr.get(taskGr.topic_type.cb_topic_id)) {
              if (topicGr.getValue('sys_policy') == 'read' && topicGr.getValue('source_topic_id') == null) {
                  sm.put('type', 'oob');
              } else {
                  sm.put('type', 'custom');
              }
          }
          GCFCollector.recordUsageEvent('conversational_analytics', 'virtual_agent', 'topic_completion', sm);
      } catch (err) {
          var message = "Metric collection for topic completion failed: " + err.message;
          this.logger.error(message);
      }
  },

  //Events

  addTopicEvent: function(topicGr) {
      this._buildTopicEvent(topicGr);
      this.conversation['Events'].push(this.topicEvent);
  },

  _addTopicNodeEvent: function(node, taskGr) {
      this._buildTopicNodeEvent(node, taskGr);
  },

  _buildTopicNodeEvent: function(node, taskGr) {
      // Only for Static Choice and Boolean nodes

      // Check if an event was already created for this node
      var topicNodeEventCreated = this.conversation['Events'].filter(function(event) {
          return event['Type'] === 'Topic Node' && event['Properties']['Topic Node Id'] === node.id;
      }).pop();

      if (topicNodeEventCreated) return;

      var nodeValues = this._getSelectedValuesForNode(node, taskGr);
      var self = this;

      nodeValues.forEach(function(nodeVal) {
          var topicNodeEvent = {
              'Properties': {}
          };
          topicNodeEvent['Name'] = self._valueCheck(node.name);
          topicNodeEvent['Time'] = self._getDuration(nodeVal.time);
          topicNodeEvent['Type'] = 'Topic Node';
          topicNodeEvent['Properties']['Topic Id'] = taskGr.topic_type.sys_id + '';
          topicNodeEvent['Properties']['Topic Name'] = taskGr.topic_type.name + '';
          topicNodeEvent['Properties']['Topic Node Id'] = node.id;
          topicNodeEvent['Properties']['Type'] = nodeVal.type; // 'choice' | 'boolean'
          topicNodeEvent['Properties']['Selected Value'] = nodeVal.value;

          self.conversation['Events'].push(topicNodeEvent);
      });
  },

  // Only returns values for Static and Boolean type nodes, else []
  _getSelectedValuesForNode: function(node, taskGr) {
      var selectedValuesForNode = [];

      // try-catch the whole flow since we're parsing the sys_cb_topic graph JSON, and it could change in future
      try {
          // Find out the type of the topic node from the "sys_cb_topic" record
          var cbTopic = new GlideRecord('sys_cb_topic');
          if (!cbTopic.get(taskGr.topic_type.cb_topic_id))
              return selectedValuesForNode;
          var graph;

          try {
              graph = JSON.parse(cbTopic.getValue('graph'));
          } catch (e) {
              this._logDebug('Error parsing sys_cb_topic graph: {}', e.toString());
              return selectedValuesForNode;
          }

          // get the first "goal" of the graph, and then it's "nodes"
          var goalId = Object.keys(graph.goals)[0]; // either "primary", or some UUID for older topics
          if (!goalId) {
              this._logDebug('No goal found in the sys_cb_topic graph');
              return selectedValuesForNode;
          }
          var nodes = graph.goals[goalId].nodes || [];
          var currentNode = nodes[node.id];

          if (currentNode && currentNode.variableId) {
              var nodeType = graph.variables[currentNode.variableId].varType;
              var nodeVarName = graph.variables[currentNode.variableId].name;
              var nodeLabel = graph.variables[currentNode.variableId].label;

              if (nodeType === 'boolean' || nodeType === 'choice') {

                  // Get the runtime messages that were user's inputs for these nodes
                  var messagesGr = new GlideRecord('sys_cs_message');
                  messagesGr.addQuery('task', taskGr.getValue('sys_id'));
                  messagesGr.addQuery('direction', 'inbound');
                  messagesGr.query();

                  while (messagesGr.next()) {
                      try {
                          var nodePayload = JSON.parse(messagesGr.getValue('payload'));
                          var nodeUiType = nodePayload.uiType; // Boolean and Picker
                          if (!nodeUiType)
                              continue;

                          if (
                              (nodePayload.model && nodePayload.model.name === nodeVarName)
                              // ensure we're picking the right message(s) for the current node
                              &&
                              (
                                  (nodeUiType === 'Picker' && nodeType === 'choice') || // sys_cs_message and graph node uses different values ('Picker' vs 'choice')
                                  (nodeUiType === 'Boolean' && nodeType === 'boolean') // sys_cs_message and graph node uses different values ('Boolean' vs 'boolean')
                              )
                          ) {
                              // we found a run-time message for this node, now get the user's input value for this input node
                              selectedValuesForNode.push({
                                  type: nodeType,
                                  value: nodePayload.value,
                                  time: messagesGr.getValue('sys_created_on')
                              });
                          }
                      } catch (e) {
                          this._logDebug('Error extracting user input values for node from messages: {}', e.toString());
                      }
                  }
              }
          }
          return selectedValuesForNode;
      } catch (e) {
          this._logDebug('Error getting the selected values for the node', e.toString());
          return selectedValuesForNode;
      }
  },

  _buildTopicEvent: function(taskGr) {
      this.topicEvent = {
          'Properties': {}
      };
      this.topicEvent['Name'] = this._valueCheck(taskGr.topic_type.name);
      this.topicEvent['Time'] = this._getDuration(taskGr.sys_created_on);
      this.topicEvent['Type'] = 'Topic';
      this.topicEvent['Properties']['Topic Id'] = this._valueCheck(taskGr.topic_type.sys_id);
      var subCategory = this._valueCheck(taskGr.topic_type.type);
      this.topicEvent['Properties']['Sub Category'] = subCategory;
      if (subCategory == 'SETUP_TOPIC')
          this.topicEvent['Properties']['Setup Topic Type'] = this._valueCheck(this._getSetupTopicType(taskGr.topic_type));
      else if (subCategory == 'TOPIC_BLOCK') {
          this.topicEvent['Properties']['Parent Topic Name'] = this._valueCheck(taskGr.calling_task.topic_type.name);
          this.topicEvent['Properties']['Parent Topic Id'] = this._valueCheck(taskGr.calling_task.topic_type.sys_id);
      }
      this.topicEvent['Properties']['Channel Name'] = this.conversationChannel;
      this.topicEvent['Properties']['End State'] = this.conversationEndState;
      this.topicEvent['Properties']['Live Agent Transfer'] = this.liveAgentTranferFlag;
      this._addTopicCategoryEvents(taskGr);
      this.topicEvent['Properties']['NLU Intent'] = this._getNluIntent(this.topicEvent['Properties']['Topic Id']);
      this.topicEvent['Properties']['Application'] = this._valueCheck(taskGr.topic_type.sys_scope.getDisplayValue("sys_scope"));
      this.topicEvent['Properties']['Category'] = this._valueCheck(taskGr.topic_type.design_category.getDisplayValue("design_category"));
  },

  _getNluIntent: function(topicId) {
      var arrayUtil = new global.ArrayUtil();
      if (arrayUtil.indexOf(this.predictions.AUTOSELECTED, topicId) > -1)
          return this.INTENT_AUTO_SELECTED;

      if (arrayUtil.indexOf(this.predictions.CORRECT, topicId) > -1)
          return this.INTENT_CORRECT;

      if (arrayUtil.indexOf(this.predictions.INCORRECT, topicId) > -1)
          return this.INTENT_INCORRECT;
  },

  _getSetupTopicType: function(topicName) {
      var gr = new GlideRecord('sys_cs_context_profile_topic');
      gr.addQuery('topic', topicName);
      gr.query();
      gr.next();
      return gr.getDisplayValue('topic_type');
  },

  _addTopicCategoryEvents: function(taskGr) {
      var topicCategoryIds = taskGr.topic_type.design_category;
      var topicCategoryGr = new GlideRecord('sys_cb_topic_category');
      topicCategoryGr.addEncodedQuery('sys_idIN' + topicCategoryIds);
      topicCategoryGr.query();
      while (topicCategoryGr.next()) {
          this._buildTopicCategoryEvent(topicCategoryGr, taskGr);
          this.conversation['Events'].push(this.topicCategoryEvent);
      }
  },

  _buildTopicCategoryEvent: function(topicCategoryGr, taskGr) {
      this.topicCategoryEvent = {
          'Properties': {}
      };
      var name = this._valueCheck(topicCategoryGr.name);
      this.topicCategoryEvent['Name'] = name;
      this.topicCategoryEvent['Time'] = this._getDuration(taskGr.sys_created_on);
      this.topicCategoryEvent['Type'] = 'Topic Category';
      this.topicCategoryEvent['Properties']['Topic Id'] = this._valueCheck(taskGr.topic_type.sys_id);
      this.topicCategoryEvent['Properties']['Topic Name'] = this._valueCheck(taskGr.topic_type.name);
      this.topicCategoryEvent['Properties']['End State'] = this.conversationEndState;
      this.topicCategoryEvent['Properties']['Live Agent Transfer'] = this.liveAgentTranferFlag;
      this.topicCategoryEvent['Properties']['Channel Name'] = this.conversationChannel;
  },

  buildIntentEvent: function(intentFeedBackGr, intentGr, topicLangGr) {
      this.intentEvent = {
          'Properties': {}
      };
      var predictionType = this._getPredictionType(intentFeedBackGr);
      this.intentEvent['Name'] = this.getIntentName(predictionType);
      var auditCompletedTime = intentFeedBackGr.audit_log && intentFeedBackGr.audit_log.completed;
      this.intentEvent['Time'] = this._getDuration(auditCompletedTime);
      this.intentEvent['Type'] = 'Intent';
      this.intentEvent['Properties']['Id'] = this._valueCheck(intentGr.getValue('sys_id'));
      this.intentEvent['Properties']['Selected'] = this._getIntentStatus(intentFeedBackGr).toString();
      this.intentEvent['Properties']['Topic Id'] = topicLangGr.cs_topic_id && this._valueCheck(topicLangGr.cs_topic_id.sys_id);
      this.intentEvent['Properties']['Topic Name'] = topicLangGr.cs_topic_id && this._valueCheck(topicLangGr.cs_topic_id.name);
      this.intentEvent['Properties']['Duration'] = intentFeedBackGr.audit_log.duration.toString();
      this.intentEvent['Properties']['NLU Provider'] = intentFeedBackGr.getDisplayValue('nlu_provider');
      this._addToPredictions(predictionType, this.intentEvent['Properties']['Topic Id']);
      return this.intentEvent;
  },

  addIntentsToConversation: function(nluIntents) {
      this.conversation['Events'].push.apply(this.conversation['Events'], nluIntents);
  },

  _getIntentStatus: function(intentFeedBackGr) {
      return this.util.getBooleanValue(intentFeedBackGr.getValue('selected'));
  },

  getIntentName: function(type) {
      var name;
      if (type == this.INTENT_AUTO_SELECTED)
          name = 'Intent Auto Selected';
      else if (type == this.INTENT_CORRECT)
          name = 'Intent Correctly Detected';
      else if (type == this.INTENT_INCORRECT)
          name = 'Intent Incorrectly Detected';
      else if (type == this.INTENT_UNSUPPORT)
          name = 'Intent Unsupported';
      return name;
  },

  _addToPredictions: function(predictionType, topicId) {
      if (!predictionType || !topicId)
          return;
      if (predictionType == this.INTENT_AUTO_SELECTED)
          this.predictions.AUTOSELECTED.push(topicId);
      else if (predictionType == this.INTENT_CORRECT)
          this.predictions.CORRECT.push(topicId);
      else if (predictionType == this.INTENT_INCORRECT)
          this.predictions.INCORRECT.push(topicId);
  },

  _getPredictionType: function(intentFeedBackGr) {
      var shown = this.util.getBooleanValue(intentFeedBackGr.getValue('shown'));
      var selected = this.util.getBooleanValue(intentFeedBackGr.getValue('selected'));
      var autoSelected = this.util.getBooleanValue(intentFeedBackGr.getValue('auto_selected'));
      var prediction = intentFeedBackGr.getValue('prediction');
      if (!prediction || !prediction.length)
          return this.INTENT_UNSUPPORT;
      if (autoSelected)
          return this.INTENT_AUTO_SELECTED;
      if (selected)
          return this.INTENT_CORRECT;
      if (shown)
          return this.INTENT_INCORRECT;
  },

  preProcessCustomEvent: function(eventGr) {
      this.event = {
          'id': eventGr.getValue('sys_id'),
          'custom_name': eventGr.getValue('custom_name'),
          'custom_type': eventGr.getValue('custom_type'),
          'apply_post_fetch_filter': eventGr.getValue('apply_post_fetch_filter'),
          'event_time': eventGr.getValue('event_time'),
          'event_time_field': eventGr.getValue('event_time_field'),
          'propIds': []
      };
  },

  addCustomEvent: function(sourceGr, eventGr) {
      var customChatEvent = this._buildCustomEvent(sourceGr, eventGr);
      if (customChatEvent)
          this.conversation['Events'].push(customChatEvent);
  },

  _buildCustomEvent: function(sourceGr, eventGr) {
      if (!this._isCustomEventValid(sourceGr, eventGr)) {
          this._logDebug('Event ignored because post_fetch_filter script filters all data', []);
          return;
      }
      var customName = this._valueCheck(this._getCustomEventName(sourceGr, eventGr));
      if (!customName) {
          this._logDebug("Event ignored because event name is empty", []);
          return;
      }

      var customChatEvent = {
          'Properties': {}
      };
      customChatEvent['Name'] = customName;
      customChatEvent['Type'] = this._valueCheck(this._getCustomEventType(sourceGr, eventGr));
      customChatEvent['Time'] = this._getDuration(this._getEventTime(sourceGr, eventGr));

      var eventPropGr = new GlideRecord('sn_ci_analytics_event_prop');
      eventPropGr.addQuery('event', eventGr.getValue('sys_id'));
      eventPropGr.query();
      while (eventPropGr.next()) {
          var value;

          var evaluator = new GlideScopedEvaluator();
          evaluator.putVariable('sourceGr', sourceGr);

          // this required as GlideScopedEvaluator.evaluateScript checks for isValidRecord()
          if (eventPropGr.advanced == 1)
              value = evaluator.evaluateScript(eventPropGr, "value_script", null);
          else {
              eventPropGr.field = 'sourceGr.' + eventPropGr.field;
              value = evaluator.evaluateScript(eventPropGr, "field", null);
          }
          customChatEvent['Properties'][eventPropGr.getValue('name')] = this._valueCheck(value);
      }

      return customChatEvent;
  },

  _getCustomEventName: function(sourceGr, eventGr) {
      var customNameFlag = this.util.getBooleanValue(this.event.custom_name);

      if (customNameFlag) {
          var evaluator = new GlideScopedEvaluator();
          evaluator.putVariable('sourceGr', sourceGr);
          return evaluator.evaluateScript(eventGr, 'custom_name_script', null);
      } else
          return eventGr.getValue('name');
  },

  _getCustomEventType: function(sourceGr, eventGr) {
      var customTypeFlag = this.util.getBooleanValue(this.event.custom_type);

      if (customTypeFlag) {
          var evaluator = new GlideScopedEvaluator();
          evaluator.putVariable('sourceGr', sourceGr);
          var res = evaluator.evaluateScript(eventGr, 'custom_type_script', null);
          if (!res)
              return 'Custom';
          else
              return res;
      } else
          return eventGr.getValue('type');
  },

  _isCustomEventValid: function(sourceGr, eventGr) {
      var postFetchFilterApplied = this.util.getBooleanValue(this.event.apply_post_fetch_filter);
      if (!postFetchFilterApplied)
          return true;
      var evaluator = new GlideScopedEvaluator();
      evaluator.putVariable('sourceGr', sourceGr);
      return evaluator.evaluateScript(eventGr, 'post_fetch_filter', null);
  },

  _getEventTime: function(sourceGr, eventGr) {
      var eventTime = sourceGr.getValue('sys_created_on');
      var eventTimeConfigured = this.util.getBooleanValue(this.event.event_time);
      if (eventTimeConfigured) {
          var evaluator = new GlideScopedEvaluator();
          evaluator.putVariable('sourceGr', sourceGr);
          var oldValue = eventGr.event_time_field.toString();
          eventGr.event_time_field = 'sourceGr.' + oldValue;
          eventTime = evaluator.evaluateScript(eventGr, "event_time_field", null);
          eventGr.event_time_field = oldValue; // reset to oldvalue as eventGr used multiple times
      }
      return eventTime;
  },

  _getTopicNodeDuration: function(topicNode) {
      var nodeDuration = topicNode.start_time - this.conversationStartTimeInMillis;
      if (nodeDuration > this.conversation['Duration'])
          return this.conversation['Duration'];
      return nodeDuration;
  },

  _getDuration: function(createdDate) {
      return createdDate ? new GlideDateTime(createdDate).getNumericValue() - this.conversationStartTimeInMillis : 0;
  },

  // call this to make sure we don't send '' or null to appsee
  _valueCheck: function(value) {
      if (gs.nil(value))
          return undefined;
      return value.toString();
  },

  processOverride: function(overrideGr, conversationId) {
      var key = overrideGr.getValue('key');
      var value;
      var convGr = new GlideRecord('sys_cs_conversation');
      convGr.get(conversationId);

      var evaluator = new GlideScopedEvaluator();
      evaluator.putVariable('convGr', convGr);

      if (overrideGr.advanced == 1)
          value = evaluator.evaluateScript(overrideGr, "value_script", null);
      else {
          overrideGr.value_field = 'convGr.' + overrideGr.value_field;
          value = evaluator.evaluateScript(overrideGr, "value_field", null);
      }
      value = gs.nil(value) ? undefined : value;
      this._logDebug('Override Property {2} value {3}', [key, value]);
      var type = overrideGr.getValue('type');
      if (type == 'attribute')
          this.conversation[key] = (typeof value == 'object') ? value.toString() : value;
      else if (type == 'dynamicproperty')
          if (typeof value == 'object') {
              for (var item in value) {
                  this.conversation['DynamicProperties'][item] = value[item];
              }
          }
      else {
          this.conversation['DynamicProperties'][key] = value;
      }

  },

  // appends the default template msg.
  _logDebug: function(msg_template, params) {
      if (this.logger.isDebugEnabled()) {
          var arrayUtil = new global.ArrayUtil();
          var defaultParams = [this.conversationId, CABatchProcessor.batchId];
          var argParams = arrayUtil.convertArray(params);
          var mergedArray = arrayUtil.union(defaultParams, argParams);
          msg_temp = "Conversation:{0}, BatchId:{1}, " + msg_template;
          this.logger.debug(msg_temp, mergedArray);
      }

  },

  type: 'CAPayloadBuilder'
};

Sys ID

4415cf5853fa10101dccddeeff7b128f

Offical Documentation

Official Docs: