This article is a supplement to the ServiceNow documentation. For full documentation please refer ServiceNow official website
Checkout our NEW Video Channel you can like and subscribe too!

Use Case: When a assigned Sev 1 / Sev 2 Case for the assignment groups say Test-INTEL,Test-CORE-99, SLA resolution and response is crossed by 50% and 75% and breached, we have to get notified on slack channel #test-core-client-internal with below message

Example - Ticket number - Customer - Short Description - Response SLA clock is ticking(“%”SLA already Crossed) on this “Severity” ticket in “Engineer’s Name” name. Please address quickly.

Slackusecase190120212.JPG

Slackusecase190120216.JPG

Solution

Steps:

  1. We have to get the webhook of the Slack channel where we want to post.

Webhook looks like below: https://hooks.slack.com/services/AC3R7L2XY/U01ECPSG484/39XLtLLqtXtOEQMNpjEZJlXI

  1. We have an OOTB workflow “Default SLA workflow” that emits two events:

     - sla.warning
        
     - sla.warning.breach
    

    Slackusecase190120218.JPG

  2. From the above workflow we can observe that when sla reaches 50% and 75% “sla.warning” events are getting triggered. Also when SLA breaches then “sla.warning.breach” is getting triggered.

  3. We are going to listen to these events and write two script actions to send notification is slack channel.

Slackusecase190120219.JPG

Script Action

Slack Notif-SLA-50/75-Core/Client:

Send notification to slack when sla reaches 50% and 75%

For 50% and 75% ,sla are using same event(sla.warning) so that we are create one script action for 50% and 75%

Slackusecase1901202110.JPG Slackusecase1901202111.JPG

 
 //gs.log('eventname------>' + event.name + 'instance---->' + current.sys_id);

var isPlainTextRequired = false;
var emoji = "stopwatch";
var record = getCaseDetails(current.task);
var thresholdTolerance = 10;
var targetType = current.sla.getDisplayValue().split("-")[0];
var slackLinkByGroup = {};

//Slack-Group Mapping
slackLinkByGroup['TEST-CORE-SERVICES-INTERNAL'] = "https://hooks.slack.com/services/Tdc3fghjGM/A0fhbnjVDUG/yCFNpZCdfdd8MJIuerdf";
slackLinkByGroup['TEST-CORE-CLIENT-INTERNAL'] = "https://hooks.slack.com/services/erdfgresrM2GM/B01FCfgh4666/44cvbgfdrertztOEQEYtyghff";
//slackLinkByGroup['SNOW-IN-TEAM'] = "https://hooks.slack.com/services/TC3R7M2GM/yuhjgfde0B7JB/LZopuiylkZ5nJsshM";

var coreServiceGroups = ["TEST-CORE-DATABASE", "TEST-CORE-Backup"];
var coreClientGroups = ["TEST-CORE-UNIX", "TEST-CORE-INTEL"];
var priorityList = ["1", "2"];
var allowedInstances = ["https://dev456.service-now.com/"];
var instanceURL = gs.getProperty('glide.servlet.uri');

if (record.next()) {

    var threshold = calculatedThreshold(parseInt(current.percentage));

    if (isPriorityAllowed(record.priority.toString()) &&
        !isProcessed(threshold, current.sys_id) &&
        isAllowedInstance() &&
        current.stage == "in_progress") {

        var colorCode = evaluateColorCode(threshold);
        var thresholdText = threshold != 0 ? threshold + "% SLA already Crossed" : "Reopened";
        var assignedTo = record.assigned_to.getDisplayValue() == "" ? "Not Assigned" : record.assigned_to.getDisplayValue();
        var headingText = targetType + "SLA clock is ticking ( " + thresholdText + " ) on this Severity " + record.priority + " ticket. " + "Assignee - " + assignedTo + ". Please address quickly.";
        var ticketNumber = record.number;
        var customer = record.account.getDisplayValue();
        var shortDescription = record.short_description;
        var incidentLink = instanceURL + "sn_customerservice_case.do?sys_id=" + record.sys_id;
        var slacklink = evaluateSlackLink(record.assignment_group.getDisplayValue());
        var fallBackText = ticketNumber + "-" + customer + "-" + shortDescription + "-" + headingText;
        var plainTextMessage = "{\"text\": \"" + fallBackText + "\"}";
        var assignmentGroup = record.assignment_group.getDisplayValue();

        var template = "{ \"attachments\":[";
        template = template + "{\"fallback\": \"%fallBackText%\",";
        template = template + "\"color\": \"%colorCode%\",";
        template = template + "\"fields\": [";
        template = template + "{\"title\": \":%emoji%: %headingText%\", \"short\": false },";
        template = template + "{\"value\": \"Ticket number : `%ticketNumber%`\", \"short\": false },";
        template = template + "{\"value\": \"Customer : `%customer%`\", \"short\": false },";
        template = template + "{\"value\": \"Assignment Group : `%assignmentGroup%`\", \"short\": false },";
        template = template + "{\"value\": \"Short Description: `%shortDescription%`\", \"short\": false },";
        template = template + "{\"value\": \"_Click here to view it_ : <%incidentLink%|*incident*>\",\"short\": false }";
        template = template + "]}";
        template = template + "]}";

        template = template.replace("%fallBackText%", fallBackText);
        template = template.replace("%colorCode%", colorCode);
        template = template.replace("%headingText%", headingText);
        template = template.replace("%ticketNumber%", ticketNumber);
        template = template.replace("%customer%", customer);
        template = template.replace("%assignmentGroup%", assignmentGroup);
        template = template.replace("%shortDescription%", shortDescription);
        template = template.replace("%incidentLink%", incidentLink);
        template = template.replace("%emoji%", emoji);

        var finalMessage = isPlainTextRequired ? plainTextMessage : template;

        try {

            //Used to prevent duplicate message getting sent for 100%, please dont remove
            gs.log("Threshold for SLA -->" + threshold + '-' + current.sys_id, threshold + "-" + current.sys_id);

            var request = new sn_ws.RESTMessageV2();
            request.setHttpMethod('post');
            request.setRequestBody(finalMessage);
            request.setEndpoint(slacklink);
            var response = request.execute();
        } catch (ex) {
            //Nothing to do
        }
    }
}

function getCaseDetails(caseSysId) {
    var record = new GlideRecord('sn_customerservice_case');
    record.addQuery('sys_id', caseSysId);
    record.query();
    return record;
}

function isProcessed(threshold, taskId) {
    var taskList = new GlideRecord('syslog');
    taskList.addQuery('source', threshold + '-' + taskId);
    taskList.addEncodedQuery("sys_created_onONLast 15 minutes@javascript:gs.beginningOfLast15Minutes()@javascript:gs.endOfLast15Minutes()");
    taskList.query();
    if (taskList.next()) {
        return true;
    } else {
        return false;
    }
}

function calculatedThreshold(percentage) {
    if (percentage >= 50 && percentage < (50 + thresholdTolerance))
        return 50;
    else if (percentage >= 75 && percentage < (75 + thresholdTolerance))
        return 75;
    else if (percentage >= 100 && percentage < (100 + thresholdTolerance))
        return 100;
    //Reopened SLAs shows abnormal event triggering behaviour.In such case default result to 0
    else
        return 0;
}

function isEventForManager(eventParam2) {
    return eventParam2 == "manager";
}

function evaluateColorCode(threshold) {
    /**
    #F1C40F --> yellow
    #E67E22 --> orange
    #A93226 --> red
    **/

    var code = "";
    switch (threshold) {
        case 50:
            code = "#F1C40F";
            break;
        case 75:
            code = "#E67E22";
            break;
        default:
            code = "#A93226";
    }
    return code;
}

function evaluateSlackLink(groupName) {
    if (coreServiceGroups.indexOf(groupName) !== -1) {
        return slackLinkByGroup['TEST-CORE-SERVICES-INTERNAL'];
    }
    if (coreClientGroups.indexOf(groupName) !== -1) {
        return slackLinkByGroup['TEST-CORE-CLIENT-INTERNAL'];
    }
}

function isPriorityAllowed(priority) {
    return priorityList.indexOf(priority) !== -1;
}

function isAllowedInstance() {
    return allowedInstances.indexOf(instanceURL) !== -1;
}
 
 

Script Action

Slack Notif-SLA Breach-Core/Client

Send notification to slack when sla reaches 100%

For 100%, Sla are using different event so we are creating different script action.

Slackusecase1901202114.JPG Slackusecase1901202119.JPG

//gs.log('eventname------>' + event.name + 'instance---->' + current.sys_id);

//Fallback to plain text if rich text format doesnt support
var isPlainTextRequired = false;

//Emoji icon to use.This text needs to be compatible with Slack emojis
var emoji = "stopwatch";

//Get case details from task sla
var record = getCaseDetails(current.task);

// This tolerance will define the range to take floor value from the range of values
// For example - percentage is 53.6 then assume 50.
var thresholdTolerance = 10;

//Extracts SLA type response/resolution from sla name
var targetType = current.sla.getDisplayValue().split("-")[0];

//Captures event paramter 2
var eventParam2 = event.parm2;

//List of all slack webhooks.Add new entry below and map it to a name
var slackLinkByGroup = {};

//Slack-Group Mapping
slackLinkByGroup['TEST-CORE-SERVICES-INTERNAL'] = "https://hooks.slack.com/services/DE4567YHJK2GM/DSF54RTHG/yFDSERTWS56TG";
slackLinkByGroup['TEST-CORE-CLIENT-INTERNAL'] = "https://hooks.slack.com/services/GHTR567EDHGF/39YBtCYqYUIOJK895GHFD";
//slackLinkByGroup['SNOW-IN-TEAM'] = "https://hooks.slack.com/services/TCfdfghjk01ENVIKIKJ/JH4567YHJGHRTS";

//List of Groups.The releationship of these groups to slack link is done in  evaluateSlackLink function
var coreServiceGroups = ["TEST-CORE-DATABASE", "TEST-CORE-Backup"];
var coreClientGroups = ["TEST-CORE-UNIX", "TEST-CORE-INTEL"];
var priorityList = ["1", "2"];
var allowedInstances = ["https://dev567.service-now.com/"];
var instanceURL = gs.getProperty('glide.servlet.uri');

if (record.next()) {

    //Calculate the threshold based on the current percentage
    var threshold = calculatedThreshold(parseInt(current.percentage));
    var thresholdText = threshold != 0 ? threshold + "% SLA already Crossed" : "Reopened";

    //Send message only if the event is not fired for a manager and is not already send and with allowed priority.
    if (!isEventForManager(eventParam2) &&
        !isProcessed(threshold, current.sys_id) &&
        isPriorityAllowed(record.priority.toString()) &&
        isAllowedInstance() &&
        current.stage == "in_progress") {

        //Determines the colorcode based on threshold
        var colorCode = evaluateColorCode(threshold);

        //All below variables are constructed before applied to the template
        var assignedTo = record.assigned_to.getDisplayValue() == "" ? "Not Assigned" : record.assigned_to.getDisplayValue();
        var headingText = targetType + "SLA clock is ticking ( " + thresholdText + " ) on this Severity " + record.priority + " ticket. " + "Assignee - " + assignedTo + ". Please address quickly.";
        var ticketNumber = record.number;
        var customer = record.account.getDisplayValue();
        var shortDescription = record.short_description;
        var incidentLink = instanceURL + "sn_customerservice_case.do?sys_id=" + record.sys_id;
        var slacklink = evaluateSlackLink(record.assignment_group.getDisplayValue());
        var fallBackText = ticketNumber + "-" + customer + "-" + shortDescription + "-" + headingText;
        var plainTextMessage = "{\"text\": \"" + fallBackText + "\"}";
        var assignmentGroup = record.assignment_group.getDisplayValue();

        //The template for slack message
        var template = "{ \"attachments\":[";
        template = template + "{\"fallback\": \"%fallBackText%\",";
        template = template + "\"color\": \"%colorCode%\",";
        template = template + "\"fields\": [";
        template = template + "{\"title\": \":%emoji%: %headingText%\", \"short\": false },";
        template = template + "{\"value\": \"Ticket number : `%ticketNumber%`\", \"short\": false },";
        template = template + "{\"value\": \"Customer : `%customer%`\", \"short\": false },";
        template = template + "{\"value\": \"Assignment Group : `%assignmentGroup%`\", \"short\": false },";
        template = template + "{\"value\": \"Short Description: `%shortDescription%`\", \"short\": false },";
        template = template + "{\"value\": \"_Click here to view it_ : <%incidentLink%|*incident*>\",\"short\": false }";
        template = template + "]}";
        template = template + "]}";

        //Fill all the values in the template
        template = template.replace("%fallBackText%", fallBackText);
        template = template.replace("%colorCode%", colorCode);
        template = template.replace("%headingText%", headingText);
        template = template.replace("%ticketNumber%", ticketNumber);
        template = template.replace("%customer%", customer);
        template = template.replace("%assignmentGroup%", assignmentGroup);
        template = template.replace("%shortDescription%", shortDescription);
        template = template.replace("%incidentLink%", incidentLink);
        template = template.replace("%emoji%", emoji);

        var finalMessage = isPlainTextRequired ? plainTextMessage : template;

        try {

            //Used to prevent duplicate message getting sent for 100%, please dont remove
            gs.log("Threshold for SLA -->" + threshold + '-' + current.sys_id, threshold + "-" + current.sys_id);

            //Send message to slack
            var request = new sn_ws.RESTMessageV2();
            request.setHttpMethod('post');
            request.setRequestBody(finalMessage);
            request.setEndpoint(slacklink);
            var response = request.execute();
        } catch (ex) {
            //Nothing to do
        }
    }
}

function getCaseDetails(caseSysId) {
    var record = new GlideRecord('sn_customerservice_case');
    record.addQuery('sys_id', caseSysId);
    record.query();
    return record;
}

function isProcessed(threshold, taskId) {
    var taskList = new GlideRecord('syslog');
    taskList.addQuery('source', threshold + '-' + taskId);
    taskList.addEncodedQuery("sys_created_onONLast 15 minutes@javascript:gs.beginningOfLast15Minutes()@javascript:gs.endOfLast15Minutes()");
    taskList.query();
    if (taskList.next()) {
        return true;
    } else {
        return false;
    }
}

function calculatedThreshold(percentage) {
    if (percentage >= 50 && percentage < (50 + thresholdTolerance))
        return 50;
    else if (percentage >= 75 && percentage < (75 + thresholdTolerance))
        return 75;
    else if (percentage >= 100 && percentage < (100 + thresholdTolerance))
        return 100;
    //Reopened SLAs shows abnormal event triggering behaviour.In such case default result to 0
    else
        return 0;
}

function isEventForManager(eventParam2) {
    return eventParam2 == "manager";
}

function evaluateColorCode(threshold) {
    /**
    #F1C40F --> yellow
    #E67E22 --> orange
    #A93226 --> red
    **/

    var code = "";
    switch (threshold) {
        case 50:
            code = "#F1C40F";
            break;
        case 75:
            code = "#E67E22";
            break;
        default:
            code = "#A93226";
    }
    return code;
}

function evaluateSlackLink(groupName) {
    if (coreServiceGroups.indexOf(groupName) !== -1) {
        return slackLinkByGroup['TEST-CORE-SERVICES-INTERNAL'];
    }
    if (coreClientGroups.indexOf(groupName) !== -1) {
        return slackLinkByGroup['TEST-CORE-CLIENT-INTERNAL'];
    }
}

function isPriorityAllowed(priority) {
    return priorityList.indexOf(priority) !== -1;
}

function isAllowedInstance() {
    return allowedInstances.indexOf(instanceURL) !== -1;
}
    Content