Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleanup a bit and use hubot-botframework for MS Teams support #164

Merged
merged 11 commits into from
Mar 29, 2019
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
node_modules/

coverage/
npm-debug.log
113 changes: 113 additions & 0 deletions lib/format_data.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ SlackFormatter.prototype.normalizeCommand = function(command) {
return command;
};

SlackFormatter.prototype.normalizeAddressee = function(msg) {
return {
name: msg.message.user.name,
room: msg.message.room
};
};

/*
MattermostFormatter.
*/
Expand Down Expand Up @@ -88,6 +95,48 @@ MattermostFormatter.prototype.normalizeCommand = function(command) {
return command;
};

MattermostFormatter.prototype.normalizeAddressee = function(msg) {
return {
name: msg.message.user.name,
room: msg.message.room
};
};

/*
MSTeamsFormatter
*/
function MSTeamsFormatter(robot) {
this.robot = robot;
}

MSTeamsFormatter.prototype.formatData = function(data) {
this.robot.logger.debug("Got data in formatData: " + JSON.stringify(data));

// Remove starting newlines
data = data.replace(/^\n/g, '');
// Replace single newlines with double newlines
data = data.replace(/([^\n])\n([^\n])/g, "$1\n\n$2");

return data;
};

MSTeamsFormatter.prototype.formatRecepient = function(recepient) {
this.robot.logger.debug("Got recipient in formatRecepient: " + JSON.stringify(recepient));
return recepient;
};

MSTeamsFormatter.prototype.normalizeCommand = function (command) {
this.robot.logger.debug("Got command in normalizeCommand: " + JSON.stringify(command));
return command;
}

MSTeamsFormatter.prototype.normalizeAddressee = function(msg) {
return {
name: msg.message.user.name,
room: msg.message.room
};
};

/*
HipChatFormatter.
*/
Expand All @@ -114,6 +163,13 @@ HipChatFormatter.prototype.normalizeCommand = function(command) {
return command;
};

HipChatFormatter.prototype.normalizeAddressee = function(msg) {
return {
name: msg.message.user.mention_name,
room: msg.message.user.jid
};
};

/*
RocketChatFormatter.
*/
Expand All @@ -138,6 +194,54 @@ RocketChatFormatter.prototype.normalizeCommand = function(command) {
return command;
};

RocketChatFormatter.prototype.normalizeAddressee = function(msg) {
return {
name: msg.message.user.name,
room: msg.message.room
};
};

/*
SparkFormatter.
*/
function SparkFormatter(robot) {
this.robot = robot;

// Limit the size of a message.
this.truncate_length = env.ST2_MAX_MESSAGE_LENGTH;
}

SparkFormatter.prototype.formatData = function(data) {
if (utils.isNull(data)) {
return "";
}
if (this.truncate_length > 0) {
// The ellipsis argument is only to preserve backwards compatibility, as the
// truncate function switched from using '...' (three period characters
// forming and ellipsis) in truncate 1.x to '…' (a single Unicode ellipsis
// character) in truncate 2+.
// Switching to using the new default ellipsis ('…') probably won't break
// anything.
data = truncate(data, this.truncate_length, {ellipsis: '...'});
}
return data;
};

SparkFormatter.prototype.formatRecepient = function(recepient) {
return recepient;
};

SparkFormatter.prototype.normalizeCommand = function(command) {
return command;
};

SparkFormatter.prototype.normalizeAddressee = function(msg) {
return {
name: msg.message.user.name,
room: msg.message.user.room
};
};

/*
DefaultFormatter.
*/
Expand Down Expand Up @@ -172,11 +276,20 @@ DefaultFormatter.prototype.normalizeCommand = function(command) {
return command;
};

DefaultFormatter.prototype.normalizeAddressee = function(msg) {
return {
name: msg.message.user.name,
room: msg.message.room
};
};

var formatters = {
'slack': SlackFormatter,
'matteruser': MattermostFormatter,
'mattermost': MattermostFormatter,
'botframework': MSTeamsFormatter,
'hipchat': HipChatFormatter,
'spark': SparkFormatter,
'rocketchat': RocketChatFormatter,
'default': DefaultFormatter
};
Expand Down
73 changes: 71 additions & 2 deletions lib/post_data.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ function SlackDataPostHandler(robot, formatter) {
SlackDataPostHandler.prototype.postData = function(data) {
var recipient, attachment_color, split_message,
attachment, pretext = "";
var envelope, robot = this.robot;
var envelope,
// We capture robot here so the `sendMessage` closure captures the
// correct `this`
robot = this.robot;

if (data.whisper && data.user) {
recipient = data.user;
Expand Down Expand Up @@ -190,10 +193,54 @@ SlackDataPostHandler.prototype.postData = function(data) {
}
};

/*
MSTeamsPostHandler.
*/
function MSTeamsPostHandler(robot, formatter) {
this.robot = robot;
this.formatter = formatter;
}

MSTeamsPostHandler.prototype.postData = function(data) {
var messages_to_send,
// We capture robot here so the `sendMessage` closure captures the
// correct `this`
robot = this.robot,
split_message = utils.splitMessage(data.message);

if (data.extra && data.extra.botframework) {
robot.logger.warning(util.format('The extra.botframework attribute of aliases is not used yet.'));
}

if (split_message.pretext) {
var text = this.formatter.formatData(split_message.text);
messages_to_send = [split_message.pretext, text];
} else {
messages_to_send = [this.formatter.formatData(data.message)];
}

// We define a recursive closure that calls itself with the next data to send
// after a timeout. This approximates sending synchronous (sequential) HTTP
// requests.
var sendMessage = function (i) {
robot.adapter.send(data.context, messages_to_send[i]);

if (messages_to_send.length > ++i) {
setTimeout(function () { sendMessage(i); }, 300);
}
};

sendMessage(0);

return;
};

/*
MattermostDataPostHandler.
*/
function MattermostDataPostHandler(robot, formatter) {
// We capture robot here so the `sendMessage` closure captures the correct
// `this`
this.robot = robot;
this.formatter = formatter;
}
Expand All @@ -202,18 +249,23 @@ MattermostDataPostHandler.prototype.postData = function(data) {
var recipient, attachment_color, split_message,
attachment, pretext = "";

// If we are supposed to whisper to a single user, use a direct message
if (data.whisper && data.user) {
recipient = data.user;
} else {
} else { // Otherwise, message the channel
recipient = data.channel;
// If we aren't supposed to whisper, then we at least at-mention the user
pretext = (data.user && !data.whisper) ? util.format('@%s: ', data.user) : "";
}

// Use the color specified in the `extra` block
if (data.extra && data.extra.color) {
attachment_color = data.extra.color;
} else {
// Assume success, and use the success color
attachment_color = env.ST2_MATTERMOST_SUCCESS_COLOR;

// Try to detect execution failure and use the failure color instead
if (data.message.indexOf("status : failed") > -1) {
attachment_color = env.ST2_MATTERMOST_FAIL_COLOR;
}
Expand All @@ -222,16 +274,24 @@ MattermostDataPostHandler.prototype.postData = function(data) {
split_message = utils.splitMessage(this.formatter.formatData(data.message));

if (split_message.text) {
// Default values
var content = {
color: attachment_color,
"mrkdwn_in": ["text", "pretext"],
};
// Override the default values with values from `data.extra.mattermost`
if (data.extra && data.extra.mattermost) {
for (var attrname in data.extra.mattermost) { content[attrname] = data.extra.mattermost[attrname]; }
}

// We capture robot here so the `sendMessage` closure captures the correct
// `this`
var robot = this.robot;
var chunks = split_message.text.match(/[\s\S]{1,3800}/g);

// We define a recursive closure that calls itself with the next data to
// send after a timeout. This approximates sending synchronous (sequential)
// HTTP requests.
var sendChunk = function (i) {
content.text = chunks[i];
content.fallback = chunks[i];
Expand All @@ -244,6 +304,10 @@ MattermostDataPostHandler.prototype.postData = function(data) {
attachment = {
room: recipient,
attachments: content.attachments ? content.attachments : content,
// There is likely a bug here - `split_message.text` being a true-y
// value does not imply that `split_message.pretext` is also non-empty,
// but we unconditionally set `text` to
// `pretext + split_message.pretext` on the first message
text: i === 0 ? pretext + split_message.pretext : null
};
robot.emit('slack-attachment', attachment);
Expand All @@ -267,6 +331,9 @@ function HipchatDataPostHandler(robot, formatter) {

HipchatDataPostHandler.prototype.postData = function(data) {
var recipient, split_message, formatted_message,
// Special handler to try and figure out when a hipchat message
// is a whisper:
whisper = data.whisper && (data.channel.indexOf('@') > -1),
pretext = "";

recipient = data.channel;
Expand Down Expand Up @@ -320,6 +387,7 @@ SparkDataPostHandler.prototype.postData = function(data) {
}

recipient = this.formatter.formatRecepient(recipient);
// TODO: Pull attributes from data.extra.spark before pulling them from data.extra
recipient.extra = data.extra;
text += this.formatter.formatData(data.message);

Expand Down Expand Up @@ -429,6 +497,7 @@ DefaultFormatter.prototype.postData = function(data) {

var dataPostHandlers = {
'slack': SlackDataPostHandler,
'botframework': MSTeamsPostHandler,
'matteruser': MattermostDataPostHandler,
'mattermost': MattermostDataPostHandler,
'hipchat': HipchatDataPostHandler,
Expand Down
24 changes: 0 additions & 24 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,37 +110,13 @@ function enable2FA(action_alias) {
action_alias.extra.security.twofactor !== undefined;
}

function normalizeAddressee(msg, adapter) {
var name = msg.message.user.name;
if (adapter === "hipchat") {
// Hipchat users aren't pinged by name, they're
// pinged by mention_name
name = msg.message.user.mention_name;
}
var room = msg.message.room;
if (room === undefined) {
if (adapter === "hipchat") {
room = msg.message.user.jid;
}
}
if (adapter === "spark") {
room = msg.message.user.room;
name = msg.message.user.name;
}
return {
name: name,
room: room
};
}

exports.isNull = isNull;
exports.getExecutionHistoryUrl = getExecutionHistoryUrl;
exports.parseUrl = parseUrl;
exports.getTextChunks = getTextChunks;
exports.splitMessage = splitMessage;
exports.constructFromAttrs = constructFromAttrs;
exports.enable2FA = enable2FA;
exports.normalizeAddressee = normalizeAddressee;
exports.SLACK_MAX_MESSAGE_SIZE = SLACK_MAX_MESSAGE_SIZE;
exports.DISPLAY = DISPLAY;
exports.REPRESENTATION = REPRESENTATION;
12 changes: 8 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "hubot-stackstorm",
"description": "A hubot plugin for integrating with StackStorm event-driven infrastructure automation platform.",
"version": "0.9.2",
"version": "0.9.3",
"author": "StackStorm, Inc. <info@stackstorm.com>",
"license": "Apache-2.0",
"keywords": [
Expand All @@ -22,8 +22,8 @@
},
"dependencies": {
"cli-table": "<=1.0.0",
"coffee-register": "^2.2.0",
"coffeescript": "^2.3.2",
"coffee-register": "1.0.0",
"coffee-script": "1.12.7",
"lodash": "^4.17.11",
"rsvp": "^4.8.4",
"st2client": "^1.1.1",
Expand All @@ -41,7 +41,7 @@
"gulp-mocha": "^6.0.0",
"gulp-plumber": "^1.2.0",
"hubot": "^3.1.1",
"hubot-help": "^0.2.2",
"hubot-help": "0.2.2",
"hubot-mock-adapter": "^1.1.1",
"jshint": "^2.7.0",
"jshint-stylish": "^2.0.0",
Expand All @@ -63,6 +63,10 @@
],
"exclude": [
"tests"
],
"reporter": [
"html",
"text"
]
},
"engines": {
Expand Down
Loading