Skip to content

Commit 4664604

Browse files
Merge branch 'ServiceNowDevProgram:main' into Number-validation-script
2 parents 0219153 + f670d24 commit 4664604

File tree

4 files changed

+204
-0
lines changed

4 files changed

+204
-0
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
This script identifies tickets similar to a given ticket in ServiceNow based on text similarity of short_description and description fields, optionally boosting the score for matching categories or assignment groups. It is intended for background script execution for testing, analysis, or automation purposes.
2+
3+
**Features:**
4+
1) Compares a source ticket against other tickets in the same table (incident by default).
5+
2) Computes Jaccard similarity between tokenized text fields.
6+
3) Applies bonus points for matching category and assignment_group.
7+
4) Returns a sorted list of similar tickets with score, number, caller_id and short_description.
8+
5) Supports top N results and a minimum score filter.
9+
10+
11+
**Usage:**
12+
1) Paste the script in scripts-background.
13+
2) Make changes to the sys_id of your ticket in line no. 3
14+
3) click run to get an output as below
15+
16+
**Output:**
17+
* *** Script: === Similar Tickets to: INC0010005 ===
18+
* *** Script: INC0010006 | Score: 1.08 | undefined | Sai Test INC0008112
19+
* *** Script: INC0010004 | Score: 0.58 | undefined | Sai Test INC0009005
20+
* *** Script: INC0009009 | Score: 0.161 | undefined | Unable to access the shared folder.test
21+
* *** Script: INC0008001 | Score: 0.08 | undefined | ATF:TEST2
22+
* *** Script: INC0000020 | Score: 0.08 | undefined | I need a replacement iPhone, please
23+
* *** Script: INC0000031 | Score: 0.08 | undefined | Need help with Remedy. Can we configure UI?
24+
* *** Script: INC0000040 | Score: 0.08 | undefined | JavaScript error on hiring page of corporate website
25+
* *** Script: INC0010002 | Score: 0.08 | undefined |
26+
* *** Script: INC0000057 | Score: 0.08 | undefined | Performance problems with wifi
27+
* *** Script: INC0010003 | Score: 0.08 | undefined |
28+
29+
**Explanation of the output:**
30+
1) First Line contains the ticket which you have provided as a sys_id.
31+
2) Next lines contains the ticket which contain ticket no. | score | caller_id | short_description.
32+
3) If you keenly observe there are few tickets that do not have similar short description / description with scores as 0.08 but still in output the reason for this is their category and assignment group still matches with the compared ticket.
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
(function() {
2+
var table = 'incident'; //can be used for other tickets as well
3+
var sourceSysId = 'f4755b82c3203210348bbd33e40131cb'; // sys_id of the ticket which is used to find similar tickets
4+
var limit = 10; // top N results
5+
var minScore = 0.05;
6+
7+
function tokensFromText(text) {
8+
if (!text) return [];
9+
text = text.toLowerCase().replace(/[^a-z0-9\s]/g, ' ');
10+
var raw = text.split(/\s+/);
11+
var stop = {
12+
'the':1,'and':1,'for':1,'with':1,'that':1,'this':1,'from':1,'have':1,'has':1,'was':1,'were':1,
13+
'a':1,'an':1,'is':1,'in':1,'on':1,'of':1,'to':1,'it':1,'as':1,'by':1,'be':1,'are':1
14+
};
15+
var map = {};
16+
for (var i=0;i<raw.length;i++) {
17+
var t = raw[i].trim();
18+
if (!t || t.length<3 || stop[t]) continue; // skip very short tokens
19+
t = t.replace(/(ing|ed|s)$/,'');
20+
map[t] = (map[t]||0)+1;
21+
}
22+
return Object.keys(map);
23+
}
24+
25+
function jaccardScore(tokensA, tokensB) {
26+
var setA={}, setB={};
27+
tokensA.forEach(function(t){setA[t]=1;});
28+
tokensB.forEach(function(t){setB[t]=1;});
29+
var inter=0, uni=0;
30+
for (var t in setA){ if(setB[t]) inter++; uni++; }
31+
for (var t2 in setB){ if(!setA[t2]) uni++; }
32+
return uni===0 ? 0 : inter/uni;
33+
}
34+
35+
// Get source record
36+
var src = new GlideRecord(table);
37+
if (!src.get(sourceSysId)) {
38+
gs.error("Source record not found");
39+
return;
40+
}
41+
42+
var sourceText = [src.short_description, src.description].join(" ");
43+
var sourceTokens = tokensFromText(sourceText);
44+
if (sourceTokens.length === 0) {
45+
gs.print("No meaningful text to compare");
46+
return;
47+
}
48+
49+
// Find candidate incidents
50+
var gr = new GlideRecord(table);
51+
gr.addActiveQuery();
52+
gr.addQuery('sys_id','!=',sourceSysId);
53+
gr.setLimit(300);
54+
gr.query();
55+
56+
var results = [];
57+
while (gr.next()) {
58+
var candidateText = [gr.short_description, gr.description].join(" ");
59+
var candidateTokens = tokensFromText(candidateText);
60+
var score = jaccardScore(sourceTokens, candidateTokens);
61+
62+
if (src.category == gr.category) score += 0.05;
63+
if (src.assignment_group == gr.assignment_group) score += 0.03;
64+
65+
if (score >= minScore) {
66+
results.push({
67+
number: gr.number.toString(),
68+
short_description: gr.short_description.toString(),
69+
score: parseFloat(score.toFixed(3))
70+
});
71+
}
72+
}
73+
74+
results.sort(function(a,b){return b.score - a.score;});
75+
76+
// Print top results
77+
gs.print("=== Similar Tickets to: " + src.number + " ===");
78+
results.slice(0, limit).forEach(function(r) {
79+
gs.print(r.number + " | Score: " + r.score + " | " + r.caller_id + " | " + r.short_description);
80+
});
81+
82+
})();
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# User Impersonation Activity Logger
2+
3+
A ServiceNow server-side utility that automatically creates a log when an action is performed under impersonation, helping distinguish between admin-added and user-added notes.
4+
5+
# Challenge
6+
7+
The challenge lies in distinguishing between actions performed by administrators impersonating users and those performed by the users themselves. Without a reliable way to track impersonation activity, it becomes difficult to ensure transparency and accountability in ticket histories. This lack of clarity can lead to confusion during audits, misinterpretation of updates, and potential compliance risks. Addressing this issue is critical to maintaining trust and operational efficiency.
8+
9+
## Description
10+
11+
This script identifies if the current user session is under impersonation (e.g., an admin impersonating another user).
12+
If true, it automatically appends a message in the **Logs** indicating that the note was added during impersonation.
13+
This improves auditability and clarity when reviewing ticket histories.
14+
15+
## Functionality
16+
17+
The User Impersonation Activity Logger provides the following capabilities:
18+
- Detects if the current user is impersonating another user
19+
- Automatically appends a log message stating the impersonation context
20+
- Works in **Business Rule** and Global Scoped Tables
21+
- Logs both actual user and impersonated user details
22+
- Provides clear distinction for audit and tracking
23+
24+
## Usage Instructions
25+
26+
### Add as Business Rule
27+
28+
```javascript
29+
// When: before update
30+
// Table: incident
31+
// Script:
32+
(function executeRule(current, previous /*null when async*/) {
33+
if (new GlideImpersonate().isImpersonating()) { // Check if the user is impersonating
34+
if (current.comments.changes() || current.work_notes.changes()) { // Check if comments or work notes have changed
35+
let actualUserName = gs.getImpersonatingUserDisplayName();
36+
let impersonatedUserName = gs.getUserDisplayName();
37+
let logMessage = `User Impersonation Activity Detected:
38+
Timestamp : ${ new GlideDateTime()}
39+
Actual User: ${actualUserName}
40+
Impersonated User: ${impersonatedUserName}
41+
Comments added: ${current.comments || 'NA'}
42+
Work Notes added: ${current.work_notes || 'NA'}`;
43+
gs.info(logMessage);
44+
}
45+
}
46+
})(current, previous);
47+
```
48+
49+
50+
## Prerequisites
51+
52+
- Need admin access to check the impersonation logs later
53+
54+
55+
## Dependencies
56+
57+
- GlideSystem API
58+
- GlideImpersonate API
59+
- gs.getSession()
60+
- GlideDateTime() API
61+
62+
63+
## Category
64+
65+
Server-Side Components / Business Rules / User Impersonation Activity Logger
66+
67+
## Hacktoberfest 2025
68+
69+
Created as first Contribution for ServiceNow Hacktoberfest 2025 🎃
70+
71+
## License
72+
73+
MIT License
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
(function executeRule(current, previous /*null when async*/) {
2+
if (new GlideImpersonate().isImpersonating()) {
3+
// Check if the user is impersonating
4+
if (current.comments.changes() || current.work_notes.changes()) {
5+
// Check if comments or work notes have changed
6+
let actualUserName = gs.getImpersonatingUserDisplayName();
7+
let impersonatedUserName = gs.getUserDisplayName();
8+
let logMessage = `User Impersonation Activity Detected:
9+
Timestamp : ${new GlideDateTime()}
10+
Actual User: ${actualUserName}
11+
Impersonated User: ${impersonatedUserName}
12+
Comments added: ${current.comments || "NA"}
13+
Work Notes added: ${current.work_notes || "NA"}`;
14+
gs.info(logMessage);
15+
}
16+
}
17+
})(current, previous);

0 commit comments

Comments
 (0)