Last active
June 27, 2020 18:54
-
-
Save made2591/9db86b9c3256ea60620b7923f9ee9297 to your computer and use it in GitHub Desktop.
AWS Lambda Node.js as slack command handler (with specified grammar)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const AWS = require('aws-sdk'); | |
const Slack = require("slack-node"); | |
AWS.config.region = process.env.REGION; | |
const lambda = new AWS.Lambda(); | |
// #################################################### | |
// ################# SECURITY CHECK ################### | |
// #################################################### | |
// check if slack token is equal to env var | |
function checkToken(token) { | |
return token == process.env.SLACK_TOKEN; | |
} | |
// check if slack team id is equal to env var | |
function checkTeamID(teamID) { | |
return teamID == process.env.SLACK_TEAM_ID; | |
} | |
// check if slack channel id is equal to env var | |
function checkChannelID(channelID) { | |
return channelID == process.env.SLACK_CHANNEL_ID; | |
} | |
// check if slack user id is equal to env var | |
function checkUserID(userID) { | |
return userID == process.env.SLACK_USER_ID; | |
} | |
// check if slack command string is equal to env var | |
function checkSlackBotCommand(command) { | |
return command == process.env.SLACK_BOT_COMMAND; | |
} | |
// error if some security check fail | |
const errorStandardMessage = { 'text' : 'Some is wrong...you supposts to be slack, but you are not.' }; | |
// #################################################### | |
// ################### PARSER LIB ##################### | |
// #################################################### | |
function checkSecurity(event) { | |
return new Promise(function(resolve, reject) { | |
if (!checkToken(event.token) || | |
!checkTeamID(event.team_id) || | |
!checkChannelID(event.channel_id) || | |
!checkUserID(event.user_id) || | |
!checkSlackBotCommand(event.command)) { | |
reject(errorStandardMessage); | |
} else { | |
resolve(event.text); | |
} | |
}); | |
} | |
// create payload from text slack command | |
function parseText(text) { | |
return new Promise(function(resolve, reject) { | |
if (text) { | |
// parsed parameters | |
var payloads = []; | |
// splits contexts | |
var contexts = text.split(";").map(function(context) { | |
return context.trim(); | |
}); | |
// for each context | |
contexts.forEach(function(contextValue, contextIndex) { | |
// isolate context | |
var context = contextValue; | |
// splits actions | |
var actions = context.split(",").map(function(actionContent) { | |
return actionContent.trim(); | |
}); | |
actions.forEach(function(contextContent, contextIndex) { | |
var action = ""; | |
var parameters = []; | |
// splits context name and action + parameters | |
var contentValues = contextContent.split("->").map(function(contentValue) { | |
return contentValue.trim(); | |
}); | |
// isolate context from actions + parameters | |
if (contentValues.length > 1) { | |
// isolate context or action from parameters | |
if (contextIndex == 0) { | |
context = contentValues[0]; | |
} else { | |
action = contentValues[0]; | |
} | |
var remainingContent = contentValues[1]; | |
// splits action and parameters | |
var actionParameters = remainingContent.split(" ").map(function(contentValue) { | |
return contentValue.trim(); | |
}); | |
// isolate context from actions + parameters | |
if (actionParameters.length > 0) { | |
// isolate first element as action | |
action = actionParameters[0]; | |
// isolate remaining element as parameters | |
if (actionParameters.length > 1) { | |
parameters = actionParameters.slice(1, actionParameters.length); | |
} | |
payloads.push({ context : context, action : action, parameters : parameters }); | |
} | |
} | |
}); | |
}); | |
resolve(payloads); | |
} else { | |
reject(errorStandardMessage); | |
} | |
}); | |
} | |
// check for integrity with respect to common configuration hosted in S3 bucket | |
function checkIntegrity(payloads) { | |
console.log(payloads); | |
return new Promise(function(resolve, reject) { | |
// S3 handler | |
var S3 = new AWS.S3({ | |
maxRetries: 0, | |
region: process.env.REGION | |
}); | |
S3.getObject({ | |
Bucket: process.env.S3_BUCKET_NAME, | |
Key: process.env.S3_BUCKET_OBJECT, | |
}, function(error, data) { | |
if (error) { | |
reject(error); | |
} else { | |
// parse context from S3 config file | |
var contexts = JSON.parse(data.Body.toString('utf-8')); | |
// payload to remove | |
var toRemove = []; | |
// for each passed payload | |
payloads.forEach(function(payload, index) { | |
// if context is admitted | |
if (Object.keys(contexts).includes(payload['context'])) { | |
// if action is allowed in context | |
if (!Object.keys(contexts[payload['context']]).includes(payload['action'])) { | |
toRemove.push(index); | |
} | |
} else { | |
toRemove.push(index); | |
} | |
}); | |
// remove invalid payload | |
toRemove.forEach(function(i) { | |
payloads.splice(i, 1); | |
}); | |
resolve(payloads); | |
} | |
}); | |
}); | |
} | |
function invokeLambda(payloads) { | |
return new Promise(function(resolve, reject){ | |
var params = { | |
FunctionName: process.env.INTERNAL_LAMBDA_NAME, | |
InvocationType: 'RequestResponse', | |
Payload: JSON.stringify(payloads) | |
}; | |
lambda.invoke(params, function(error, data) { | |
if (error) { | |
reject(error); | |
} else { | |
resolve(data); | |
} | |
}); | |
}); | |
} | |
function sendMessage(url, data) { | |
// Return new promise | |
return new Promise(function(resolve, reject) { | |
var slack = new Slack(); | |
slack.setWebhook(url); | |
slack.webhook({ | |
channel: process.env.SLACK_AWS_CHANNEL, | |
username: process.env.SLACK_AWS_BOT_NAME, | |
text: data | |
}, function(error, response) { | |
if (error) { | |
console.log(error); | |
reject(error); | |
} else { | |
resolve(response); | |
} | |
}); | |
}); | |
} | |
function publishActionOnSNSTopic(message) { | |
var sns = new AWS.SNS(); | |
sns.publish({ | |
Message: message, | |
TopicArn: process.env.VPC_ACTIONS_TOPIC | |
}, function(err, data) { | |
if (err) { | |
console.log(err.stack); | |
return; | |
} | |
console.log('push sent'); | |
console.log(data); | |
context.done(null, 'Function Finished!'); | |
}); | |
} | |
// export handler function | |
exports.handler = (event, context, callback) => { | |
console.log('0', JSON.stringify(event)); | |
checkSecurity(event).then(function(text) { | |
sendMessage(event.response_url, 'Security check passed: ok').then(function(slackResponse) { | |
parseText(text).then(function(payloads) { | |
console.log('a', JSON.stringify(payloads)); | |
sendMessage(event.response_url, 'Parsed command: '+JSON.stringify(payloads)).then(function(slackResponse) { | |
checkIntegrity(payloads).then(function(payloadAdmitted) { | |
console.log('a', JSON.stringify(payloadAdmitted)); | |
sendMessage(event.response_url, 'Admitted command: '+JSON.stringify(payloadAdmitted)).then(function(slackResponse) { | |
publishActionOnSNSTopic(JSON.stringify(payloadAdmitted)); | |
// invokeLambda(payloadAdmitted).then(function(lambdaResponse) { | |
// sendMessage(event.response_url, 'Lambda response: '+JSON.stringify(payloadAdmitted)).then(function(slackResponseInternal) { | |
// console.log('9', slackResponseInternal); | |
// callback(null, slackResponseInternal); | |
// }).catch((error) => { console.log('8', error); callback(null, error); }); | |
// }).catch((error) => { console.log('7', error); callback(null, error); }); | |
}).catch((error) => { console.log('6', error); callback(null, error); }); | |
}).catch((error) => { console.log('5', error); callback(null, error); }); | |
}).catch((error) => { console.log('4', error); callback(null, error); }); | |
}).catch((error) => { console.log('3', error); callback(null, error); }); | |
}).catch((error) => { console.log('2', error); callback(null, error); }); | |
}).catch((error) => { console.log('1', error); callback(null, error); }); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment