ServiceNow – Adding an Animated VIP Icon

I wanted to find a method that would make an incident stand out if it was logged for a VIP user. There is a field decoration (I believe its out of the box) that shows like this:

But I felt that we could get a bit more “pop”. I wanted the engineer to have visibility that this was a VIP incident the entire time they were looking at the record. For that reason I created this (with some help from ChatGPT).

Utilising a client script, I was able to display an animated VIP icon that shows no matter where the engineer is on the form. It looks like this. The image is also pulsing, but I can’t show that in a screenshot so you’ll have to trust me!

I downloaded a VIP icon that I liked the look of. Personally I was a fan of this one https://www.flaticon.com/free-icon/vip_3791579.

Next, you need to add it to the image library in ServiceNow. Open “Images” under “System UI”. Click “New” and upload your image. Name the image “vip_flash.png”. It should look like this when done:

Next, we need to create a client script that will utilise it. Open “Client Scripts” under “System Definition”. Click “New” and configure your client script as follows (you can adjust whatever you need to, I’m focusing on Incident for this one):

NameFlash field – VIP
DescriptionShow an animated VIP popup on the incident form.
TableIncident
UI TypeDesktop
TypeonLoad
Isolate ScriptFalse

Then for the script, add this. Effectively it uses g_form.getReference to see if the user is a VIP. If the user is, create an image element, place it and animate it. It also has a function to get the header hight values; this is to make sure it fits OK.

function onLoad() {
    // Get the reference of the caller_id field
    g_form.getReference('caller_id', function(caller) {
        // Check if the caller is a VIP
        // Double quote to convert to boolean
        if (caller.vip == "true") {
            createVIPAnimation();
        }
    });

    // Setup the onresize event handler
    window.onresize = function() {
        adjustVIPPosition();
    };
}

function createVIPAnimation() {
    // Create an image element to display the VIP image
    var vipImage = document.createElement('img');
    vipImage.src = 'vip_flash.png';
    vipImage.style.position = 'fixed';
    vipImage.style.right = '70px'; // Adjusted to position it on the right
    vipImage.style.width = '50px'; // Set the initial width to be small
    vipImage.style.height = '50px'; // Set the initial height to be small
    vipImage.style.zIndex = '10000';
    vipImage.style.animation = 'vipAnimation 2s infinite';
    vipImage.className = 'vipImage'; // Add a class name to the VIP image for easy selection later

    // Get the height of the header bar dynamically
    var headerHeight = getHeaderHeight();

    // Adjust the top property based on the header bar height
    vipImage.style.top = (headerHeight + 10) + 'px'; // Adding 10px margin to position it below the header

    document.body.appendChild(vipImage);

    // Create style element to hold the keyframes for the animation
    var styleElement = document.createElement('style');
    styleElement.type = 'text/css';
    styleElement.innerHTML = '@keyframes vipAnimation {' + 
                                '0% {' + 
                                    'transform: scale(1);' + 
                                '}' +
                                '50% {' +
                                    'transform: scale(1.1);' + 
                                '}' +
                                '100% {' +
                                    'transform: scale(1);' +
                                '}' +
                            '}';

    document.head.appendChild(styleElement);
}

function getHeaderHeight() {
    var tableName = g_form.getTableName();
    var headerId = tableName + '.form_header';
    var header = document.getElementById(headerId);

    return header ? header.offsetHeight : 0;
}

function adjustVIPPosition() {
    var header = document.getElementById(g_form.getTableName() + '.form_header');
    if (header) {
        var headerRect = header.getBoundingClientRect();
        var vipImage = document.querySelector('.vipImage');
        if (vipImage) {
            vipImage.style.top = (headerRect.bottom + 10) + 'px'; // 10px below the current bottom of the header
        }
    }
}

ServiceNow: Automating Fix Script Generation with a UI Action

Sorry for the title; didn’t know what to call this! Ever so often I’ll run a fix script to perform some sort of action on a GlideRecord (updating an attribute on a number of records for example).

With this idea, I created a UI action that generates a fix script template based on the current record. This script includes the basic structure for loading the specific record and leaves space for you to define the operations you want to perform on it.

To implement this, you need to create a new UI action and use the provided script below. You can customize it according to your needs. I’m sharing this in case anyone else finds it as useful as I do. Let me know if you have any thoughts or suggestions for improvement! This will only show for admins due to the conditions.

NameCreate fix script to get this record
Tableglobal
Form linktrue
Show updatetrue
Conditionsgs.hasRole(“admin”)

In the script area, add the following:

// Retrieving the relevant data from the current context: table name, record ID, and user
var tableName = current.getTableName();
var recordSysId = current.sys_id.toString();
var username = gs.getUser().getName();

// Creating a unique name for the fix script, using the retrieved username and table name
var fixScriptName = "Auto Fix Script: " + username + " - " + tableName;

// Generating the fix script, which will load a record from the table and 
// then perform operations on it (operations to be defined by the user)
var fixScript =
    "// Generated Fix Script\n" +
    "var record = new GlideRecord('" + tableName + "');\n" +
    "if (record.get('" + recordSysId + "')) {\n" +
    "    // perform operations on the record here\n" +
    "}\n";

// Logging the generated script to the system log
gs.info("Generated Fix Script: \n" + fixScript);

// Creating a new record in the 'sys_script_fix' table, to save the generated fix script
var fix_script = new GlideRecord('sys_script_fix');
fix_script.name = fixScriptName;
fix_script.description = "Automatically created via UI action.";
fix_script.script = fixScript;

// Inserting the new fix script record into the system and storing the returned sys_id
var fixScriptSysId = fix_script.insert();

// Redirecting the user to the newly created fix script record in the system
action.setRedirectURL(fix_script);

Once done, submit the UI action. You should now see a new UI action appear in the form of a link on the various records. It should look something like this:

When you run the UI action on a record you should get something like this:

ServiceNow – ChatGPT Integration – Making recommendations based on short description

Thought this might be useful for somebody. This is assuming that you have already run through this post to get the initial setup done: ServiceNow – ChatGPT Integration.

This is a very basic configuration and it may be better to do it through a flow, but as an example of how you could potentially update a task (incident in this case) and provide recommendations from ChatGPT when the description is updated. This is a fairly flexible approach so you might have other ideas!

Firstly, open up the incident table. We are going to create a new column called “ChatGPT Recommendations”. Give this column the type of “String” and a max length of 2000. Copy the column name (should be u_chatgpt_recommendations) and submit.

Open the incident form and make sure the new column is showing on the form.

We will now create a business rule to deliver the result. Configure the business rule conditions as follows:

Name:Set recommendations based on description
Table:Incident
Advanced:true (checked)
When to run
When:before
Insert:true (checked)
Update:true (checked)
Short description:changes

Now on the advanced tab we will enter the following code:

(function executeRule(current, previous /*null when async*/ ) {

    // Create an instance of the ChatGPT class
    var chatGPT = new global.ChatGPT();

    // Set the premise for the chat with the assistant. The premise helps set the context of the conversation.
    var premise = chatGPT.setPremise("You are an IT professional providing recommendations to non-technical users. You should give the recommendations only, no pretext.");

    // Create a message requesting ChatGPT to send some recommendations.
    var message1 = chatGPT.createMessage("user", "Provide some recommendations for this: " + current.description);

    // Submit the chat to the GPT-3.5 Turbo model (default). The chat consists of the premise and the user's request.
    // The 'submitChat' function accepts an array of messages which form a conversation.
    var result = chatGPT.submitChat([premise, message1]);

    // Extract only the response from the message.
    var extracted_message = chatGPT.extractAssistantMessage(result);

	// Populate our new field.
    current.u_chatgpt_recommendations = extracted_message;

})(current, previous);

Once submitted, we should be good to go! As an example, I logged a new incident about my monitor being broken. The system showed the following: