Skip to content

Instantly share code, notes, and snippets.

@thisnameissoclever
Last active January 23, 2022 00:11
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save thisnameissoclever/d88eb747db1b46195236fc630d1df9aa to your computer and use it in GitHub Desktop.
Save thisnameissoclever/d88eb747db1b46195236fc630d1df9aa to your computer and use it in GitHub Desktop.
var MSTeamsMessage = Class.create();
MSTeamsMessage.prototype = {
/**
*
* @param {string} [teamsEndpoint=gs.getProperty()] - The MS Teams endpoint for the channel you're trying to post a message to.
* You can also specify this later by calling the .setEndpoint() method.
*/
initialize : function (teamsEndpoint) {
//local init
var defaultEndpoint = gs.getProperty('ms_teams.messages.default_endpoint', '');
var restMessageName = 'MS Teams Message';
var restMethodName = 'Send message';
if (!teamsEndpoint && !defaultEndpoint) {
throw new Error(
'Endpoint not specified, and default endpoint system property (ms_teams.messages.default_endpoint) not set. ' +
'MSTeamsMessage cannot continue without an endpoint.'
)
}
//scope init
this.message_card = {};
this.teams_endpoint = (typeof teamsEndpoint == 'undefined') ? defaultEndpoint : teamsEndpoint;
this.rest_message = new sn_ws.RESTMessageV2(restMessageName, restMethodName);
//init work
this.rest_message.setStringParameterNoEscape('teams_endpoint', this.teams_endpoint);
},
sendMessage : function (overrideValidation) {
//todo: remove
gs.debug('Message card: \n' + JSON.stringify(this.message_card, null, 2));
overrideValidation = (typeof overrideValidation == 'undefined') ? false : overrideValidation;
var response, responseBody, httpStatus;
//If overrideValidation is not true and message card is invalid, halt
if (!overrideValidation && !this.message_card._validateCard()) {
throw new Error(
'Message card body failed to validate. Unable to send message. ' +
'If you think this error is invalid, call the .sendMessage() method again, and set the ' +
'overrideValidation parameter to true.'
);
}
this.rest_message.setStringParameterNoEscape('content_body', JSON.stringify(this.message_card));
try {
response = this.rest_message.execute();
responseBody = response.getBody();
httpStatus = response.getStatusCode();
} catch (ex) {
throw new Error(
'Unknown error occurred when attempting to send REST message to MS Teams. \n' +
'Error: ' + ex.message.toString()
);
}
gs.debug('TW: HTTP status ' + httpStatus);
//todo: I dunno, do something with that response info. Maybe throw an error if warranted.
},
/* SETTERS BELOW */
/** Set your message body by constructing
*
* @param {string} title - The title of the message, to be shown at the top of the message in large font.
* This can be changed later, using the .setTitle() method of the MessageCard class.
* @param {string} [messageText=] - Optionally specify some message text to be shown below the title. HTML links and markup are supported.
* @param {string} [summary='Notification from ServiceNow'] - An optional summary of the message.
* This is not actually shown to the user in most cases.
*/
setMessage : function (title, messageText, summary) {
this.message_card = new this.MessageCard(title, messageText, summary);
},
setMessageText : function (newMessageText) {
if (!this.hasOwnProperty('message_card')) {
throw new Error(
'Unable to set message text until the message has been constructed. ' +
'Call the .setMessage() method of the MSTeamsMessage class to construct the message first.'
);
}
this.message_card.setText(newMessageText);
},
addMessageData : function (dataTitle, dataValue) {
return this.message_card.addData(dataTitle, dataValue);
},
removeMessageData : function (dataTitle) {
return this.message_card.removeData(dataTitle);
},
setEndpoint : function (teamsEndpoint) {
this.teams_endpoint = teamsEndpoint;
this.rest_message.setStringParameterNoEscape('teams_endpoint', this.teams_endpoint);
},
addLinkButton : function (actionName, actionURI) {
return this.message_card.addLinkButton(actionName, actionURI);
},
addWhookButton : function (actionName, inputs, postURI, submitButtonName) {
return this.message_card.addWebhookButton(actionName, inputs, postURI, submitButtonName);
},
/* GETTERS BELOW */
getMessageText : function () {
return this.message_card.sections[0].text;
},
/* EXAMPLE USAGE BELOW -- NOT FOR PUBLIC USE. CONTAINS ONLY EXAMPLES. */
__exampleUsage : function () {
var teamsMessage = new MSTeamsMessage();
teamsMessage.setMessage(
'Major Incident: INC000123',
'A new major Incident has been created. Please review the details below, and respond if appropriate.'
);
teamsMessage.addMessageData(
'Incident:',
'<a href=\"https://instance_name.service-now.com/nav_to.do?uri=incident.do?sysparm_query=number=INC000123\">INC000123</a>'
);
teamsMessage.addMessageData(
'Assignee:',
'Steve Smith'
);
teamsMessage.addMessageData(
'Short description:',
'Some short description here'
);
teamsMessage.addLinkButton('View Incident', 'https://instance_name.service-now.com/nav_to.do?uri=incident.do?sysparm_query=number=INC000123');
/*teamsMessage.addWhookButton(
'Add comment',
[
//new this.inputFactory.TextInput('comments', true, 'Comments will be added to the Incident')
],
'https://instance_name.service-now.com/custom_webhook_endpoint',
'Submit'
);*/
teamsMessage.sendMessage();
//todo: test n' such
},
/* CONSTRUCTORS BELOW */
/** Construct a MessageCard object which should consist of the body of the REST message.
*
* @param {string} title - The title of the message, to be shown at the top of the message in large font.
* This can be changed later, using the .setTitle() method of the MessageCard class.
* @param {string} [messageText=] - Optionally specify some message text to be shown below the title. HTML links and markup are supported.
* @param {string} [summary='Notification from ServiceNow'] - An optional summary of the message.
* This is not actually shown to the user in most cases.
* @constructor
*/
MessageCard : function (title, messageText, summary) {
//Set default value of title property to blank string if not specified
this.title = (typeof title == 'undefined') ? '' : title;
//Set default value of summary property if not valid or not specified
this.summary = summary || 'Notification from ServiceNow';
this['@type'] = 'MessageCard';
this['@context'] = gs.getProperty('glide.servlet.uri', 'https://service-now.com/');
this.themeColor = '0072C6';
this.sections = [
{
"facts" : []
}
];
/* PUBLIC METHODS BELOW */
this.setTitle = function (newTitle) {
this.title = (typeof newTitle == 'undefined') ? '' : newTitle;
};
this.setText = function (newText) {
//Set section text to newText unless undefined. If undefined, set to blank string.
this.sections[0].text = (typeof newText == 'undefined') ? '' : newText;
return true;
};
this.addData = function (dataTitle, dataContent) {
if ((typeof dataTitle != 'string') || (typeof dataContent != 'string')) {
throw new Error('Data title and contents must both be strings. Both arguments are required.');
}
this.sections[0].facts.push(
{
"name" : dataTitle,
"value" : dataContent
}
);
return true;
};
this.removeData = function (dataTitle) {
var i, elementFound;
var facts = this.sections[0].facts;
for (i = 0; i < facts.length; i++) {
if (facts[i].name == dataTitle) {
facts.splice(i, 1);
elementFound = true;
}
}
if (!elementFound) {
throw new Error(
'Data with title ' + dataTitle + ' not found in array: \n' +
JSON.stringify(facts, null, 2)
);
}
return true;
};
this.addLinkButton = function (actionName, uri) {
var actionObj = {
"@type" : "OpenUri",
"name" : actionName,
"targets" : [
{
"os" : "default",
"uri" : uri
}
]
};
if (!this.hasOwnProperty('potentialAction')) {
this.potentialAction = [];
}
this.potentialAction.push(actionObj);
return this;
};
/** Add a button to the message that will prompt the user for some information, and then send that data to a webhook when submitted.
* todo: Build a simple button-based webhook adder method that does not accept inputs.
*
* @param {string} actionName - The name of the action itself, to show up on the button at the bottom of the message (not to be confused with the submitButtonName arg, which is the name of the button that the user clicks to submit their input and hit the webhook).
* @param {Array} inputs - An ARRAY of OBJECTS constructed using the constructors in the inputFactory object property of this class.
* @param {string} postURI - The endpoint to hit with the entered data, once the user submits their input.
* @param {string} [submitButtonName='Submit'] - Optionally, specify the name of the button that the user clicks to submit the form.
* @returns {MSTeamsMessage}
*/
this.addWebhookButton = function (actionName, inputs, postURI, submitButtonName) {
var actionObj = {
"@type" : "ActionCard",
"name" : actionName,
"inputs" : inputs,
"actions" : [
{
"@type" : "HttpPOST",
"name" : submitButtonName || 'Submit',
"target" : postURI
}
]
};
if (!this.hasOwnProperty('potentialAction')) {
this.potentialAction = [];
}
this.potentialAction.push(actionObj);
return this;
};
/* PRIVATE HELPER FUNCTIONS BELOW */
/*this._addPotentialAction = function() {
if (!this.hasOwnProperty('potentialAction')) {
this.potentialAction = [];
}
}*/
this._validateCard = function () {
if (!this.summary && !this.title) {
return false;
}
if (!this.teams_endpoint) {
return false;
}
//todo: Write additional validations
return true;
};
},
inputFactory : { //todo: Build more input constructors
TextInput : function (inputID, isMultiLine, inputTitle) {
this['@type'] = 'TextInput';
this.id = inputID;
this.isMultiLine = !!isMultiLine;
this.title = inputTitle;
},
ChoiceListInput : function (inputID, inputTitle) {
this['@type'] = 'MultichoiceInput';
this.id = inputID;
this.title = inputTitle;
this.choices = [];
this.addChoice = function (choiceLabel, choiceValue) {
this.choices.push(
{
"display" : choiceLabel,
"value" : choiceValue
}
);
return this;
};
},
DateInput : function (inputID, inputTitle) {
this['@type'] = 'MultichoiceInput';
this.id = inputID;
this.title = inputTitle;
}
},
type : 'MSTeamsMessage'
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment