Name

global.ChatInteractionUtil

Description

Provides utility methods for conversational interactions, including for the following -Access the chat requester s language -Get an interaction by group or conversation ID -Update interaction context variables or skills

Script

var ChatInteractionUtil = Class.create();
ChatInteractionUtil.prototype = {
  initialize: function() {},

  getInteractionByConversation: function(conversationID) {
      var interaction = new GlideRecord('interaction');
      interaction.get('channel_metadata_document', conversationID);
      return interaction;
  },

  getInteractionByGroup: function(groupID) {
      var interactionMetaDataDocumentID = "";
      var handOffGlideRecord = new GlideRecord('sys_cs_connect_handoff');
      if (handOffGlideRecord.get('connect_group', groupID)) {
          interactionMetaDataDocumentID = handOffGlideRecord.cs_conversation;
      } else {
          interactionMetaDataDocumentID = groupID;
      }
      var interaction = new GlideRecord('interaction');
      interaction.get('channel_metadata_document', interactionMetaDataDocumentID);
      return interaction;
  },

  getRequesterLanguage: function(interaction) {
      var requesterLanguage = gs.getSession().getLanguage() + '';
      var contextGR = new GlideRecordSecure('interaction_context');
      contextGR.addQuery('interaction', interaction.sys_id);
      contextGR.addQuery('name', 'requester_session_language');
      contextGR.query();
      if (contextGR.next()) {
          requesterLanguage = contextGR.getValue('value');
      }
      return requesterLanguage;
  },

  getAgentLanguage: function(interaction) {
      var agentLanguage = gs.getSession().getLanguage() + '';
      var contextGR = new GlideRecordSecure('interaction_context');
      contextGR.addQuery('interaction', interaction.sys_id);
      contextGR.addQuery('name', 'liveagent_session_language');
      contextGR.query();
      if (contextGR.next()) {
          agentLanguage = contextGR.getValue('value');
      }
      return agentLanguage;
  },

  getDynamicTranslateLanguage: function(interaction) {
      var dynamicTranslateProp = GlideProperties.get('com.glide.cs.dynamic.translation.enable.virtual_agent', false);
      if (dynamicTranslateProp == 'true') {
          return this.getAgentLanguage(interaction);
      } else {
          return this.getRequesterLanguage(interaction);
      }
  },

  updateInteractionContext: function(interactionID, jsonObj) {
      var keys = [];
      var contextVar = [];
      var contextVarGr = new GlideRecord("interaction_context");
      contextVarGr.addQuery("interaction", interactionID);
      contextVarGr.query();
      //store existing interaction_context records for this specific interaction
      while (contextVarGr.next()) {
          contextVar[contextVarGr.name] = {
              sysID: contextVarGr.getUniqueValue() + '',
              value: contextVarGr.value + '',
          };
      }
      // Add or update values for variables in the json
      for (var key in jsonObj) {
          keys.push(key);
          var isSkillVariable =
              (key === 'liveagent_optional_skills' || key === 'liveagent_mandatory_skills');

          //if interaction_context record does not exist, add it
          if (contextVar[key].value === undefined) {
              var newValGr = new GlideRecord("interaction_context");
              newValGr.initialize();
              newValGr.interaction = interactionID;
              newValGr.name = key;
              newValGr.value = jsonObj[key];
              newValGr.setWorkflow(isSkillVariable);
              newValGr.insert();
          } else if (contextVar[key].value != jsonObj[key]) { //if value is different, update it
              var oldValGr = new GlideRecord("interaction_context");
              oldValGr.get(contextVar[key].sysID);
              oldValGr.value = jsonObj[key];
              oldValGr.setWorkflow(isSkillVariable);
              oldValGr.update();
          }
      }
      return keys;
  },

  /**
   * Process skills when relevant changes occurred on interaction_context table that are linked to the 
   * updates of context variables (liveagent_mandatory_skills or liveagent_optional_skills). As a result,
   * skills will be updated (added or removed) in interaction_m2m_skill table. Skills stored as a value
   * of aforementioned context varaibles, in the format of comma-delimited string (sys_id list).
   *
   * Supported list format
   * ---------------------
   * Without levels: "<skill-1-sysID>,<skill-2-sysID>,..."
   * Example without levels:
   * "76d51f26b3603300290ea943c6a8dcc1,7ad51f26b3603300290ea943c6a8dcbd,c3d55f26b3603300290ea943c6a8dc15"
   *
   * With levels: "<skill-1-sysID>:<skill-1-level-sysID>,<skill-2-sysID>:<skill-2-level-sysID>,..."
   * Example with levels:
   * "76d51f26b3603300290ea943c6a8dcc1:e41a08d6b3332300290ea943c6a8dcdf,bf6497c5b3332300290ea943c6a8dc4f:e0973eaab3203300290ea943c6a8dc41"
   *
   * insert - skills will be added to interaction_m2m_skill
   * update - skills new will be added to, skills non-existent previously will be removed from, interaction_m2m_skill
   * delete - skills will be removed from interaction_m2m_skill
   *
   * @param current: current record of interaction_context, being inserted, updated or deleted
   * @param previous: previous record of interaction_context to be updated; null if insert or delete
   */
  processSkills: function(current, previous) {
      var isMandatory = false;
      switch (current.getValue('name')) {
          case 'liveagent_optional_skills':
              break;
          case 'liveagent_mandatory_skills':
              isMandatory = true;
              break;
          default:
              return;
      }

      if (current.operation() == 'insert') {
          var interactionInsert = current.getValue('interaction');
          var skillsInsert = current.getValue('value').split(',');
          this.addSkills(interactionInsert, skillsInsert, isMandatory);
      } else if (current.operation() == 'update') {
          var interactionUpdate = current.getValue('interaction');
          var curSkills = current.getValue('value').split(',');
          var prevSkills = previous.getValue('value').split(',');
          var arrayUtil = new ArrayUtil();
          var skillsToAdd = arrayUtil.diff(curSkills, prevSkills);
          var skillsToDelete = arrayUtil.diff(prevSkills, curSkills);
          this.deleteSkills(interactionUpdate, skillsToDelete, isMandatory);
          this.addSkills(interactionUpdate, skillsToAdd, isMandatory);
      } else if (current.operation() == 'delete') {
          var interactionDelete = current.getValue('interaction');
          var skillsDelete = current.getValue('value').split(',');
          this.deleteSkills(interactionDelete, skillsDelete, isMandatory);
      }
  },

  addSkills: function(interaction, skills, isMandatory) {
      for (var i = 0; i < skills.length; i++) {
          var skill = skills[i];
          var level = null;
          var splitIndex = skills[i].lastIndexOf(":");
          if (splitIndex >= 0) {
              level = skill.substring(splitIndex + 1);
              skill = skill.substring(0, splitIndex);
          }
          this.setSkill(interaction, skill, isMandatory, level);
      }
  },

  deleteSkills: function(interaction, skills, isMandatory) {
      for (var i = 0; i < skills.length; i++) {
          var skill = skills[i];
          var splitIndex = skills[i].lastIndexOf(":");
          if (splitIndex >= 0) {
              skill = skill.substring(0, splitIndex);
          }
          this.removeSkill(interaction, skill, isMandatory);
      }
  },

  /**
   * For the given interaction, insert or update the given skill mapping, 
   * including the given mandatory status and level (if provided).
   * 
   * Fail if an interaction or skill cannot be found matching the arguments.
   * 
   * Do not update the level if a skill level cannot be found that matches
   * the arguments and has a level type matching the skill.
   * 
   * @param interaction: Sys ID or number of the interaction on which to set the skill
   * @param skill: Sys ID or name of the skill to set
   * @param isMandatory: Should the skill be mandatory? 
   * @param level: Sys ID or name of the skill level to set. The level type must match the level type of the skill.
   * 
   * @return Sys ID of the inserted or updated interaction_m2m_skill record
   */
  setSkill: function(interaction, skill, isMandatory, level) {
      var interactionGr = this._sanitizeInteraction(interaction);
      var interactionId = (interactionGr == null ? interaction : interactionGr.getUniqueValue());

      var skillGr = this._sanitizeSkill(skill);
      if (skillGr == null)
          return false;
      var skillId = skillGr.getUniqueValue();

      var skillLevelId = this._sanitizeSkillLevel(skillGr, level);

      return this._setSkillUnsafe(interactionId, skillId, isMandatory, skillLevelId);
  },

  _setSkillUnsafe: function(interactionId, skillId, isMandatory, skillLevelId) {
      var isGr = new GlideRecord('interaction_m2m_skill');
      isGr.addQuery('interaction', interactionId);
      isGr.addQuery('skill', skillId);
      isGr.query();
      if (!isGr.next()) {
          isGr.initialize();
          isGr.setValue('interaction', interactionId);
          isGr.setValue('skill', skillId);
      }
      if (isMandatory != null)
          isGr.setValue('mandatory', isMandatory);
      if (skillLevelId != null)
          isGr.setValue('skill_level', skillLevelId);
      return isGr.update();
  },

  /**
   * For the given interaction, remove the given skill mapping. 
   * 
   * @param interaction: Sys ID of the interaction on which to set the skill
   * @param skill: Sys ID of the skill to set
   * @param isMandatory: (optional) Only remove the skill if its `mandatory` field matches this
   * 
   * @return Whether or not the deletion was successful
   */
  removeSkill: function(interaction, skill, isMandatory) {
      var interactionGr = this._sanitizeInteraction(interaction);
      var interactionId = (interactionGr == null ? interaction : interactionGr.getUniqueValue());

      var skillGr = this._sanitizeSkill(skill);
      if (skillGr == null)
          return false;
      var skillId = skillGr.getUniqueValue();

      return this._removeSkillUnsafe(interactionId, skillId, isMandatory);
  },

  _removeSkillUnsafe: function(interactionId, skillId, isMandatory) {
      var isGr = new GlideRecord('interaction_m2m_skill');
      isGr.addQuery('interaction', interactionId);
      isGr.addQuery('skill', skillId);
      if (isMandatory != null)
          isGr.addQuery('mandatory', isMandatory);
      isGr.query();
      if (isGr.next())
          return isGr.deleteRecord();

      var modifier = "";
      if (isMandatory != null)
          modifier = "mandatory=" + isMandatory + " ";
      gs.warn("ChatInteractionUtil: Can not find {0}mapping for interaction {1} to skill {2}",
          modifier, interactionId, skillId);
      return false;
  },

  _sanitizeInteraction: function(interaction) {
      var intGr = new GlideRecord('interaction');
      if (intGr.get(interaction))
          return intGr;

      // No need to warn in this case; interaction might not be found because
      // it is still in the process of being inserted
      return null;
  },

  _sanitizeSkill: function(skill) {
      var skillGr = new GlideRecord('cmn_skill');
      if (skillGr.get(skill))
          return skillGr;
      gs.warn("ChatInteractionUtil: Can not find skill {0}", skill);
      return null;
  },

  _sanitizeSkillLevel: function(skillGr, level) {
      if (level == null)
          return null;

      var skillLevelGr = new GlideRecord('cmn_skill_level');
      var expectedLevelType = skillGr.getValue('level_type');
      skillLevelGr.addQuery('sys_id', level)
          .addOrCondition('name', level);
      skillLevelGr.addQuery('skill_level_type', expectedLevelType);
      skillLevelGr.query();
      if (skillLevelGr.next())
          return skillLevelGr.getUniqueValue();
      gs.warn("ChatInteractionUtil: Can not find skill level {0} with skill level type {1}", level, expectedLevelType);
      return null;
  },

  sendSystemMessage: function(conversation, message, requesterLanguage, agentLanguage, displayName, internal, eventType) {
  	//if requester's language is the same as the agent's language, no need to send the message twice.
  	if (requesterLanguage == agentLanguage) {
  		conversation.sendMessage({
  			body: gs.getMessageLang(message, requesterLanguage, [displayName]),
  			system: true,
  			eventType: eventType,
  			internal: internal
  		});
  	} else {
  		//send message to agent only (in agent's language)
  		conversation.sendMessage({
  			body: gs.getMessageLang(message, agentLanguage, [displayName]),
  			visibilityType: 'agent_only',
  			system: true,
  			eventType: eventType,
  			internal: internal
  		});
  		//send message to requester (in requester's language)
  		conversation.sendMessage({
  			body: gs.getMessageLang(message, requesterLanguage, [displayName]),
  			visibilityType: 'requester_only',
  			system: true,
  			eventType: eventType,
  			internal: internal
  		});
  	}
  },

  type: 'ChatInteractionUtil'
};

Sys ID

3bbe51319fa000103f50947f842e7048

Offical Documentation

Official Docs: