From f3d949d6c9aa25e83f57eb75b4611d8111971b72 Mon Sep 17 00:00:00 2001 From: Brian Whaley Date: Thu, 28 May 2020 16:18:19 -0400 Subject: [PATCH 1/7] v3.0.0 v3.0.0 --- aws.js | 440 ++++++++++++++++++++++++++++++++++++++++++++ event.js | 385 +++++++++++++++++++++++++++++++++++++++ index.js | 141 +++++--------- joke.js | 352 +++++++++++++++++++++++++++++++++++ message.js | 139 ++++++++++++++ nerdjokes.yaml | 6 +- schedule.js | 486 +++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 1852 insertions(+), 97 deletions(-) create mode 100644 aws.js create mode 100644 event.js create mode 100644 joke.js create mode 100644 message.js create mode 100644 schedule.js diff --git a/aws.js b/aws.js new file mode 100644 index 0000000..3e7fbb1 --- /dev/null +++ b/aws.js @@ -0,0 +1,440 @@ +console.log('Loading AWS class'); + +const AWS = require("aws-sdk"); +AWS.config.update({region: 'us-east-2'}); + +const log = true ; + + +// ======================================== +// DYNAMODB CLIENT CLASS +// ======================================== + + +class DDBCLIENT { + + constructor(){ + if(log) console.log("AWS DDBCLIENT CONSTRUCTOR"); + this.DDBCLIENT = new AWS.DynamoDB.DocumentClient() ; + this.stkrS3Bucket = "stkr-bucket"; + this.stkrTokenDB = "stkr-tokens"; + this.stkrImageDB = "stkr-images"; + this.stkrLogDB = "stkr-logs"; + this.jokesTokenDB = "nerdjokes-tokens"; + this.jokesDB = "nerdjokes"; + this.jokesScheduleDB = "nerdjokes-schedule"; + this.jokesTagsDB = "nerdjokes-tags"; + this.jokesLogDB = "nerdjokes-logs"; + + return this ; + } + + + async readFromDynamo(data){ + if(log) console.log("READ FROM DYNAMO - Data : ", data); + var params = { TableName: data.tablename }; + // if(data.key) params.Key = { [data.key] : data.value }; + if(data.key) params.Key = data.key; + if(data.indexname) params.IndexName = data.indexname ; + if(data.key_cond_expr) params.KeyConditionExpression = data.key_cond_expr ; + if(data.cond_expr) params.ConditionExpression = data.cond_expr ; + if(data.filter_expr) params.FilterExpression = data.filter_expr ; + if(data.attrib_vals) params.ExpressionAttributeValues = data.attrib_vals ; + if(data.proj_expr) params.ProjectionExpression = data.proj_expr ; + if(data.select) params.Select = data.select ; + if(data.limit) params.Limit = data.limit ; + if(log) console.log("READ FROM DYNAMO - Params : ", params); + return new Promise((resolve, reject) => { + this.DDBCLIENT.query(params, (error, itemData) => { + if (error) { + console.log("READ FROM DYNAMO - Error", JSON.stringify(error)); + console.log("READ FROM DYNAMO - ERROR Code : ", JSON.stringify(error.code)); + console.log("READ FROM DYNAMO - ERROR Message : ", JSON.stringify(error.message)); + console.log("READ FROM DYNAMO - ERROR Stack : ", JSON.stringify(error.stack)); + reject(error); + } else { + if(log) console.log("READ FROM DYNAMO - Success"); + resolve(itemData); + } + }); + }); + } + + + async scanFromDynamo(data){ + if(log) console.log("SCAN FROM DYNAMO - Data : ", data); + var params = { TableName: data.tablename }; + if(data.key) params.Key = data.key; + if(data.indexname) params.IndexName = data.indexname ; + if(data.key_cond_expr) params.KeyConditionExpression = data.key_cond_expr ; + if(data.cond_expr) params.ConditionExpression = data.cond_expr ; + if(data.filter_expr) params.FilterExpression = data.filter_expr ; + if(data.attrib_names) params.ExpressionAttributeNames = data.attrib_names ; + if(data.attrib_vals) params.ExpressionAttributeValues = data.attrib_vals ; + if(data.proj_expr) params.ProjectionExpression = data.proj_expr ; + if(data.select) params.Select = data.select ; + if(data.start_key) params.ExclusiveStartKey = data.start_key; + if(data.limit) params.Limit = data.limit ; + if(log) console.log("SCAN FROM DYNAMO - Params : ", params); + return new Promise((resolve, reject) => { + this.DDBCLIENT.scan(params, (error, result) => { + if (error) { + console.log("SCAN FROM DYNAMO - Error", JSON.stringify(error)); + console.log("SCAN FROM DYNAMO - ERROR Code : ", JSON.stringify(error.code)); + console.log("SCAN FROM DYNAMO - ERROR Message : ", JSON.stringify(error.message)); + console.log("SCAN FROM DYNAMO - ERROR Stack : ", JSON.stringify(error.stack)); + reject(error); + } else { + if(log) console.log("SCAN FROM DYNAMO - Success"); + if(log) console.log("SCAN FROM DYNAMO - Results : ", result); + resolve(result); + } + }); + }); + } + + + async writeToDynamo(data){ + if(log) console.log("WRITE TO DYNAMO - Data", JSON.stringify(data)); + var params = { + TableName: data.tablename, + Item: data.item + }; + if(log) console.log("WRITE TO DYNAMO - Params", JSON.stringify(params)); + return new Promise((resolve, reject) => { + this.DDBCLIENT.put(params, (error) => { + if (error) { + console.log("WRITE TO DYNAMO - Error", JSON.stringify(error)); + console.log("WRITE TO DYNAMO - ERROR Code : ", JSON.stringify(error.code)); + console.log("WRITE TO DYNAMO - ERROR Message : ", JSON.stringify(error.message)); + console.log("WRITE TO DYNAMO - ERROR Stack : ", JSON.stringify(error.stack)); + reject(error); + } else { + if(log) console.log("WRITE TO DYNAMO - Success"); + resolve("Data written to dynamo succesfully."); + } + }); + }); + } + + + async createDynamoSet(data){ + if(log) console.log("CREATE DYNAMO SET"); + if(log) console.log("CREATE DYNAMO SET - Data", data); + return this.DDBCLIENT.createSet(data) ; + } + + + async updateToDynamo(data){ + if(log) console.log("UPDATE TO DYNAMO - Data", data); + var params = { TableName: data.tablename }; + // if(data.key) params.Key = { [data.key] : data.value }; + if(data.key) params.Key = data.key; + if(data.update_expr) params.UpdateExpression = data.update_expr ; + if(data.attrib_vals) params.ExpressionAttributeValues = data.attrib_vals ; + if(data.return_vals) params.ReturnValues = data.return_vals ; + if(log) console.log("UPDATE TO DYNAMO - Params", params); + return new Promise((resolve, reject) => { + this.DDBCLIENT.update(params, (error) => { + if (error) { + console.log("UPDATE TO DYNAMO - Error", JSON.stringify(error)); + console.log("UPDATE TO DYNAMO - ERROR Code : ", JSON.stringify(error.code)); + console.log("UPDATE TO DYNAMO - ERROR Message : ", JSON.stringify(error.message)); + console.log("UPDATE TO DYNAMO - ERROR Stack : ", JSON.stringify(error.stack)); + reject(error); + } else { + if(log) console.log("UPDATE TO DYNAMO - Success"); + resolve("Data updated to dynamo succesfully."); + } + }); + }); + } + + + async deleteFromDynamo(data){ + if(log) console.log("DELETE FROM DYNAMO - Data : ", data); + var params = { TableName: data.tablename }; + if(data.key) params.Key = data.key; + if(data.indexname) params.IndexName = data.indexname ; + if(data.key_cond_expr) params.KeyConditionExpression = data.key_cond_expr ; + if(data.cond_expr) params.ConditionExpression = data.cond_expr ; + if(data.filter_expr) params.FilterExpression = data.filter_expr ; + if(data.attrib_vals) params.ExpressionAttributeValues = data.attrib_vals ; + if(data.proj_expr) params.ProjectionExpression = data.proj_expr ; + if(log) console.log("DELETE FROM DYNAMO - Params : ", params); + return new Promise((resolve, reject) => { + this.DDBCLIENT.delete(params, (error, itemData) => { + if (error) { + console.log("DELETE FROM DYNAMO - Error", JSON.stringify(error)); + console.log("DELETE FROM DYNAMO - ERROR Code : ", JSON.stringify(error.code)); + console.log("DELETE FROM DYNAMO - ERROR Message : ", JSON.stringify(error.message)); + console.log("DELETE FROM DYNAMO - ERROR Stack : ", JSON.stringify(error.stack)); + reject(error); + } else { + if(log) console.log("DELETE FROM DYNAMO - Success"); + if(log) console.log("DELETE FROM DYNAMO - Delete Data", itemData); + resolve(itemData); + } + }); + }); + } + + + async batchDeleteFromDynamo(data){ + if(log) console.log("BATCH DELETE FROM DYNAMO - Data : ", JSON.stringify(data)); + var params = data.params ; + if(log) console.log("BATCH DELETE FROM DYNAMO - Params : ", JSON.stringify(params)); + return new Promise((resolve, reject) => { + this.DDBCLIENT.batchWrite(params, (error, itemData) => { + if (error) { + console.log("BATCH DELETE FROM DYNAMO - Error", JSON.stringify(error)); + console.log("BATCH DELETE FROM DYNAMO - ERROR Code : ", JSON.stringify(error.code)); + console.log("BATCH DELETE FROM DYNAMO - ERROR Message : ", JSON.stringify(error.message)); + console.log("BATCH DELETE FROM DYNAMO - ERROR Stack : ", JSON.stringify(error.stack)); + reject(error); + } else { + if(log) console.log("BATCH DELETE FROM DYNAMO - Success"); + if(log) console.log("BATCH DELETE FROM DYNAMO - Delete Data", itemData); + resolve(itemData); + } + }); + }); + } + + +} + + +// ======================================== +// S3 CLASS +// ======================================== + + +class S3 { + + constructor(){ + if(log) console.log("AWS S3 CONSTRUCTOR"); + this.S3 = new AWS.S3(); + return this ; + } + + async isS3Object(data){ + if(log) console.log("ISS3OBJECT - Data : ", data); + try { + const params = { + Bucket: data.bucket, + Key: data.team_id + "/" + data.filename + }; + await this.S3.headObject(params).promise(); + return true; + } catch(error) { + console.log("IS S3 OBJECT - Error", JSON.stringify(error)); + console.log("IS S3 OBJECT - ERROR Code : ", JSON.stringify(error.code)); + console.log("IS S3 OBJECT - ERROR Message : ", JSON.stringify(error.message)); + console.log("IS S3 OBJECT - ERROR Stack : ", JSON.stringify(error.stack)); + //if (error.code === 'NotFound') { + return false; + //} + } + } + + async uploadImgToS3(buffer, data){ + // ==#####== REPLACE SPACES WITH DASHES ==#####== + var filename = data.filename.replace(/\s+/g, '-'); + const params = { + Bucket: data.bucket, + Key: data.team_id + "/" + filename, //data.filename, + Body: buffer, + ContentType: "image", + ACL: 'public-read-write' + }; + return new Promise((resolve, reject) => { + this.S3.putObject(params, (error) => { + if (error) { + console.log("UPLOAD TO S3 - Error", JSON.stringify(error)); + console.log("UPLOAD TO S3 - ERROR Code : ", JSON.stringify(error.code)); + console.log("UPLOAD TO S3 - ERROR Message : ", JSON.stringify(error.message)); + console.log("UPLOAD TO S3 - ERROR Stack : ", JSON.stringify(error.stack)); + reject(error); + } else { + if(log) console.log("UPLOADIMGTOS3 - " + filename + " uploaded to " + params.Bucket + " succesfully."); + resolve(filename); + } + }); + }); + + } + + async deleteImgFromS3(data){ + const params = { + Bucket: data.bucket, + Key: data.team_id + "/" + data.filename + }; + return new Promise((resolve, reject) => { + this.S3.deleteObject(params, (error) => { + if (error) { + console.log("DELETE FROM S3 - Error", JSON.stringify(error)); + console.log("DELETE FROM S3 - ERROR Code : ", JSON.stringify(error.code)); + console.log("DELETE FROM S3 - ERROR Message : ", JSON.stringify(error.message)); + console.log("DELETE FROM S3 - ERROR Stack : ", JSON.stringify(error.stack)); + reject(error); + } else { + if(log) console.log("DELETE FROM S3 - Successful - Deleted - " + data.filename ); + resolve(data.filename); + } + }); + }); + + } + + async emptyS3Directory(data) { + const listParams = { + Bucket: data.bucket, + Prefix: data.team_id + "/" + }; + const listedObjects = await S3.listObjectsV2(listParams).promise(); + if (listedObjects.Contents.length === 0) return null; + const deleteParams = { + Bucket: data.bucket, + Delete: { Objects: [] } + }; + listedObjects.Contents.forEach(({ Key }) => { + deleteParams.Delete.Objects.push({ Key }); + }); + await this.S3.deleteObjects(deleteParams).promise(); + if (listedObjects.IsTruncated) await this.emptyS3Directory(data); + } + + async getS3ItemCount(data){ + if(log) console.log("getS3ItemCount - Event : ", data); + var params = { + Bucket: data.bucket, + Prefix: data.team_id + "/" + }; + let retdata; + let items = 0; + retdata = await this.S3.listObjectsV2(params).promise(); + items = retdata.Contents.length ; + if(log) console.log("getS3ItemCount - Image Count : ", items); + return(items); + } + + async getS3ItemList(data){ + if(log) console.log("GET S3 ITEM LIST - Event : ", data); + var params = { + Bucket: data.bucket, + Prefix: data.team_id + "/" + }; + let images = []; + return new Promise((resolve, reject) => { + this.S3.listObjectsV2(params, (error, retdata) => { + if (error) { + console.log("GET S3 ITEM LIST - Error", JSON.stringify(error)); + console.log("GET S3 ITEM LIST - ERROR Code : ", JSON.stringify(error.code)); + console.log("GET S3 ITEM LIST - ERROR Message : ", JSON.stringify(error.message)); + console.log("GET S3 ITEM LIST - ERROR Stack : ", JSON.stringify(error.stack)); + reject(error); + } else { + if(log) console.log("GET S3 ITEM LIST - Success"); + for (let index = 0; index < retdata.Contents.length; index++) { + var key = retdata.Contents[index].Key; + // images += key.replace(params.Prefix, '') + " \n"; + // var item = { text: key.replace(params.Prefix, "") , value: key.replace(params.Prefix, "") }; + var item = { "text": { "type": "plain_text", "text": key.replace(params.Prefix, "") } , value: key.replace(params.Prefix, "") } ; + images.push(item); + } + if(log) console.log("GET S3 ITEM LIST - Data : ", JSON.stringify(images)); + resolve(images); + } + }); + }); + } + +} + + +// ======================================== +// SES CLASS +// ======================================== + + +class SES { + + constructor(){ + if(log) console.log("AWS SES CONSTRUCTOR"); + this.SES = new AWS.SES({region: 'us-east-1'}); + return this ; + } + + async sendEmail(data){ + var params = { + Destination: { + ToAddresses: [ data.to ] + }, + Message: { + Body: { Text: { Data: (data.body) ? data.body : data.subject } }, + Subject: { Data: data.subject } + }, + Source: data.from + }; + return new Promise((resolve, reject) => { + this.SES.sendEmail(params, (error, emaildata) => { + if (error) { + console.log("SEND EMAIL - ERROR", JSON.stringify(error)); + console.log("SEND EMAIL - ERROR Code : ", JSON.stringify(error.code)); + console.log("SEND EMAIL - ERROR Message : ", JSON.stringify(error.message)); + console.log("SEND EMAIL - ERROR Stack : ", JSON.stringify(error.stack)); + reject(error); + } else { + if(log) console.log("SEND EMAIL - Success"); + if(log) console.log("SEND EMAIL - Success Data", emaildata); + resolve(emaildata); + } + }); + }); + } + + +} + + +// ======================================== +// LAMBDA CLASS +// ======================================== + + +class LAMBDA { + + constructor(){ + if(log) console.log("AWS LAMBDA CONSTRUCTOR"); + this.LAMBDA = new AWS.Lambda(); + return this ; + } + + async callLambda(params){ + return new Promise((resolve, reject) => { + this.LAMBDA.invoke(params, (error, data) => { + if (error) { + console.log("CALL LAMBDA - ERROR", JSON.stringify(error)); + console.log("CALL LAMBDA - ERROR Code : ", JSON.stringify(error.code)); + console.log("CALL LAMBDA - ERROR Message : ", JSON.stringify(error.message)); + console.log("CALL LAMBDA - ERROR Stack : ", JSON.stringify(error.stack)); + reject(error); + } else { + if(log) console.log("CALL LAMBDA - Success"); + if(log) console.log("CALL LAMBDA - Success Data", data); + resolve(data); + } + }); + }); + } + +} + + +module.exports = { + DDBCLIENT: DDBCLIENT, + S3 : S3, + SES : SES, + LAMBDA : LAMBDA +}; diff --git a/event.js b/event.js new file mode 100644 index 0000000..e61834b --- /dev/null +++ b/event.js @@ -0,0 +1,385 @@ +console.log('Loading Event class'); + +var AWS = require("./aws"); +var Joke = require("./joke"); +var Schedule = require("./schedule"); +var Message = require("./message"); + +const log = true ; + +module.exports = class Event { + + constructor(data){ + if(log) console.log("EVENT CONSTRUCTOR"); + if(log) console.log('NERDJOKES - Received DATA:', JSON.stringify(data)); + var temp; + this.b_token; + this.v_token = (data.stageVariables) ? data.stageVariables.VERIFICATION_TOKEN : undefined ; + this.client_id = (data.stageVariables) ? data.stageVariables.CLIENT_ID : undefined ; + this.client_secret = (data.stageVariables) ? data.stageVariables.CLIENT_SECRET : undefined ; + this.env = (data.stageVariables) ? data.stageVariables.ENV : "PROD" ; + + this.email = "nerdjokes@pixelated.tech" ; + this.email_link = `` ; + this.nerdjokes_url = "https://pixelated.tech/nerdjokes.html" ; + + if (data.httpMethod == "POST"){ + if(log) console.log("NERDJOKES - RECEIVED POST"); + if(data.headers['Content-Type'] == "application/json"){ + if(log) console.log("NERDJOKES - RECEIVED POST - JSON"); + temp = (typeof data.body == "string") ? JSON.parse(data.body) : data.body ; + for(var i in temp) this[i] = temp[i]; + } else if(data.headers['Content-Type'] == 'application/x-www-form-urlencoded' ){ + if(log) console.log("NERDJOKES - RECEIVED POST - URLENCODED"); + temp = this.querystringToJSON(data.body); + if(temp.payload) { + temp = JSON.parse(temp.payload); + if(log) console.log("NERDJOKES - RECEIVED PAYLOAD : ", JSON.stringify(temp)); + } + for(var k in temp) this[k] = temp[k]; + } + } else if (data.httpMethod == "GET"){ + if(log) console.log("NERDJOKES - RECEIVED GET"); + if(data.queryStringParameters){ + // ==#####== OAUTH ==#####== + temp = data.queryStringParameters; + for(var m in temp) this[m] = temp[m]; + if (log) console.log("NERDJOKES - Event Code : ", this.code); + } + } + + this.setTeamId(); + + if(log) console.log('NERDJOKES - Event:', JSON.stringify(this)); + return this ; + } + + +// ========================================== +// SHARED FUNCTIONS +// ========================================== + + + querystringToJSON(qs) { + var pairs = qs.split('&'); + var result = {}; + pairs.forEach(function(pair) { + pair = pair.split('='); + result[pair[0]] = decodeURIComponent(pair[1] || ''); + }); + return JSON.parse(JSON.stringify(result)); + } + + + async processSlashCommand(){ + if(log) console.log("PROCESS SLASH COMMAND - Event : ", this); + if(log) console.log("PROCESS SLASH COMMAND - Text : ", this.text); + var cmd = this.command ; + var joke, schedule ; + let result ; + let returntext ; + switch (this.text) { + case "help": + if(log) console.log("PROCESS SLASH COMMAND - Help"); + returntext = "* Type '"+cmd+"' to get a random joke to immediately share with your teammates. \n" + + "* Type '"+cmd+" bug' to get more information about submitting a bug. \n" + + "* Type '"+cmd+" support' to get more information on how to reach out for support. \n" + + "* Type '"+cmd+" getjoke' to get a random joke sent immediately. \n" + + "* Type '"+cmd+" addjoke' to recommend a new joke to be added to the collection. \n" + + "* Type '"+cmd+" addschedule' to add or edit a schedule for delivering jokes to the current channel. \n" + + "* Type '"+cmd+" deleteschedule' to delete a schedule for delivering jokes to the current channel. \n" + + "* Type '"+cmd+" version' to see what version of NerdJokes you are using. \n" ; + await this.slashCommandMessage(returntext); + result = null ; + break; + case "bug": + if(log) console.log("PROCESS SLASH COMMAND - Bug"); + returntext = "If you want to report a bug, email " + this.email_link + " or join the bug channel on the pixelated-tech.slack.com workspace." ; + await this.slashCommandMessage(returntext); + result = null ; + break; + case "support": + if(log) console.log("PROCESS SLASH COMMAND - Support"); + returntext = "If you need some support, email " + this.email_link + " or join the support channel on the pixelated-tech.slack.com workspace."; + await this.slashCommandMessage(returntext); + result = null ; + break; + case "joke": + case "random": + case "getjoke": + // ==#####== SEND RANDOM JOKE ==#####== + joke = new Joke(); + await joke.load(); + await joke.sendJoke(this); + result = null; + break; + case "getjokejson": + // ==#####== SEND RANDOM JOKE ==#####== + joke = new Joke(); + await joke.load(); + result = joke.raw ; + break; + case "addjoke": + if(log) console.log("PROCESS SLASH COMMAND - Add"); + joke = new Joke(); + await joke.load(); + await joke.addNewJoke(this); + result = null; + break; + case "addschedule": + if(log) console.log("PROCESS SLASH COMMAND - Add Schedule"); + schedule = new Schedule(); + await schedule.addNewSchedule(this); + result = null; + break; + case "deleteschedule": + if(log) console.log("PROCESS SLASH COMMAND - Delete Schedule"); + schedule = new Schedule(); + await schedule.deleteSchedule(this); + result = null; + break; + case "runschedule": + // ==#####== USED BY THE SCHEDULED JOB TO SEND ALL JOKES TO ALL CHANNELS NAD ALL WORKSPACES ==#####== + if(log) console.log("PROCESS SLASH COMMAND - Run Schedule"); + var gmt_hour = new Date().getUTCHours(); + var day = new Date().getDay(); + if(log) console.log("PROCESS SLASH COMMAND - Run Schedule - d:", day, " , hr:", gmt_hour ); + schedule = new Schedule(); + schedule.day = day ; + schedule.gmt_hour = gmt_hour ; + await schedule.runSchedule(this); + result = null; + break; + case "version": + if(log) console.log("PROCESS SLASH COMMAND - Version"); + var version = process.env.AWS_LAMBDA_FUNCTION_VERSION ; + returntext = "This is version " + version + " of NerdJokes. Enjoy!" ; + await this.slashCommandMessage(returntext); + result = null ; + break; + default: + if(log) console.log("PROCESS SLASH COMMAND - Default"); + result = "NerdJokes: Unknown Image or Command"; + } + return result; + } + + + async slashCommandMessage(text) { + if(log) console.log("SLASH COMMAND MESSAGE"); + let message_data = { + token: this.b_token, + replace_original: true, + channel: this.channel_id, + response_type: 'ephemeral' , + text: text, + b_token: this.b_token , + api_method: "chat.postMessage" , + response_url: this.response_url + }; + if(log) console.log("SLASH COMMAND MESSAGE - Result : ", message_data); + var message = new Message(message_data); + await message.slackApiPost() ; + return null; + } + + + directInstallRedirect(data){ + if(data.body == null && data.queryStringParameters == null) { + // ==#####== DIRECT INSTALL FROM APP DIRECTORY ==##### + if(log) console.log("NERDJOKES - DIRECT INSTALL FROM APP DIRECTORY "); + var url = "https://slack.com/oauth/v2/authorize" ; + var qs = "client_id=" + this.client_id + "&scope=chat:write,commands,im:write" ; + var msg = { + statusCode: 302, + headers: { "Location": url + "?" + qs }, + body: null + }; + if(log) console.log("NERDJOKES - DIRECT INSTALL FROM APP DIRECTORY - Message", msg); + return msg; + } + } + + + setTeamId(){ + if(log) console.log("SET TEAM ID "); + if(this.team_id) { } + else if(this.team) { this.team_id = this.team.id ; } + // else (this.team_id = "N/A"); + if(this.team_id) this.team_id = (this.env != "PROD") ? this.team_id + "-" + this.env : this.team_id ; + if(log) console.log("SET TEAM ID : ", this.team_id); + } + + + async getAccessToken(){ + if(log) console.log("GET ACCESS TOKEN"); + var b_token ; + if(this.team_id) { + var ddbc = new AWS.DDBCLIENT(); + var getAuth = await ddbc.readFromDynamo({ + tablename: ddbc.jokesTokenDB, + key_cond_expr: "team_id = :teamid", + attrib_vals: { ":teamid": this.team_id }, + proj_expr: "access_token" + }); + if(log) console.log("GET ACCESS TOKEN - Auth Results : ", getAuth); + b_token = await getAuth.Items[0].access_token; + if(log) console.log("GET ACCESS TOKEN - Access Token : ", b_token); + return b_token; + } + } + + + verifyURL(e_challenge, e_token, v_token) { + if(log) console.log("VERIFY URL - Event Challenge "); + var result; + // ==#####== CHALLENGE ==#####== + if (this.token === this.v_token) { + result = { challenge: this.challenge }; + } else { + result = "VERIFY URL - Verification Failed"; + } + if(log) console.log("VERIFY URL - Results : ", result); + return { + statusCode: 200, + isBase64Encoded: false, + headers: {"content-type": "application/x-www-form-urlencoded"}, + body: JSON.stringify(result) + }; + } + + + async oAuthVerify() { + if(log) console.log("OAUTH VERIFY - OAuth - App Install "); + const message_data = { + client_id: this.client_id, + client_secret: this.client_secret, + code: this.code , + api_method: "oauth.v2.access" + }; + if(log) console.log("OAUTH VERIFY - OAuth Message Data : ", message_data ); + var message = new Message(message_data); + var oauthaccess = await message.slackApiGet(); + if(log) console.log("OAUTH VERIFY - OAuth - Verify : ", oauthaccess); + this.team_id = oauthaccess.team.id; + this.setTeamId(); + if(log) console.log("OAUTH VERIFY - Set Team ID : ", this.team_id); + var ddbc = new AWS.DDBCLIENT(); + const storeAuth = await ddbc.writeToDynamo({ + tablename: ddbc.jokesTokenDB, + item: { + team_id : this.team_id , + access_token : oauthaccess.access_token , + response : JSON.stringify(oauthaccess) , + put_date : new Date().toISOString() + } + }); + if(log) console.log("OAUTH VERIFY - OAuth - Key Store : ", storeAuth); + var ses = new AWS.SES(); + const email_res = await ses.sendEmail({ + from: this.email, + to: this.email, + subject: "New NerdJokes App Install!" + }); + if(log) console.log("OAUTH VERIFY - OAuth - Email Sent : ", email_res); + // ==#####== LOG EVENT ==#####== + await this.logEvent(); + return { + statusCode: 302, + headers: { + "Location": this.nerdjokes_url + }, + body: null + }; + } + + + async cleanUninstall(){ + if(log) console.log("CLEAN UNINSTALL "); + + // ==#####== GET ALL SCHEDULES ==#####== + var ddbc = new AWS.DDBCLIENT(); + const scheds = await ddbc.readFromDynamo({ + tablename: ddbc.jokesScheduleDB, + indexname: "team_id-index", + key_cond_expr: "team_id = :teamid", + attrib_vals: { ":teamid": this.team_id }, + proj_expr: "channel_id" + }); + if(log) console.log("CLEAN UNINSTALL - App Uninstalled - Scheds", scheds); + + // ==#####== DELETE SCHEDULES ==#####== + var itemsArray = []; + scheds.Items.forEach( async (sched) => { + // scheds.Items.forEach(async function(sched) { + var newitem = { + DeleteRequest : { + Key : { team_id: this.team_id, channel_id: sched.channel_id } + } + }; + itemsArray.push(newitem); + }); + if(log) console.log("CLEAN UNINSTALL - App Uninstalled - Items", itemsArray); + var params = { + RequestItems : { [ddbc.jokesScheduleDB] : itemsArray } + }; + if(log) console.log("CLEAN UNINSTALL - App Uninstalled - Params", params); + await ddbc.batchDeleteFromDynamo({ + params: params + }); + + // ==#####== DELETE TOKEN ==#####== + await ddbc.deleteFromDynamo( { + tablename: ddbc.jokesTokenDB, + key: { "team_id" : this.team_id } + }); + if(log) console.log("CLEAN UNINSTALL - Complete"); + } + + + async logEvent(){ + if(log) console.log("LOG EVENT "); + if(this.team_id) { + // ==#####== EVENT TYPE ==#####== + var eventType = null; + if(this.command && this.text) { eventType = this.command + " " + this.text ; } + else if (this.event) { eventType = this.event.type ; } + else if (this.view) { eventType = this.view.callback_id ; } + else if (this.actions){ eventType = this.actions[0].action_id ; } + else if (this.code) { eventType = "app_installed" ; } + if(log) console.log("LOG EVENT - eventType : ", eventType); + // ==#####== USER ID ==#####== + var userId = null; + if (this.user_id) { userId = this.user_id; } + else if (this.user) { userId = this.user.id ; } + else if(this.authed_user) { userId = this.authed_user.id ; } + else { userId = "." ; } + if(log) console.log("LOG EVENT - userId : ", userId); + // ==#####== CHANNEL ID ==#####== + var channelId = null ; + if (this.channel_id){ channelId = this.channel_id; } + else if (this.channel){ channelId = this.channel.id ; } + else if (this.view && this.view.private_metadata) { channelId = JSON.parse(this.view.private_metadata).channel_id ; } + else { channelId = "." ; } + if(log) console.log("LOG EVENT - channelId : ", channelId); + // ==#####== LOG TO DYNAMO ==#####== + var ddbc = new AWS.DDBCLIENT(); + var logged = await ddbc.writeToDynamo({ + tablename: ddbc.jokesLogDB, + item: { + team_id : this.team_id , + log_id: Date.now().valueOf().toString() , + event_type : eventType , + user_id : userId , + channel_id : channelId + } + }); + if(log) console.log("LOG EVENT - Logged : ", logged); + } else { + if(log) console.log("LOG EVENT - No team_id "); + } + return null; + } + + +}; \ No newline at end of file diff --git a/index.js b/index.js index fff3a01..de46cd9 100644 --- a/index.js +++ b/index.js @@ -1,123 +1,76 @@ -console.log('Loading nerdjokes'); +console.log('Loading Nerd Jokes'); -var nerdhelpers = require("./nerdjokes-helpers"); -var awstools = require("./tools-aws"); -var slacktools = require("./tools-slack"); -let env; -let log = true; +var Event = require("./event"); +var Joke = require("./joke"); +var Schedule = require("./schedule"); +let log = true; exports.handler = async function(data, context) { // ==#####== NERDJOKES APP SETUP ==#####== - if(log) console.log('NERDJOKES - Received DATA:', JSON.stringify(data)); - const stageVars = data.stageVariables; - if(log) console.log('NERDJOKES - Received Stage Variables:', JSON.stringify(stageVars)); - let b_token; - let v_token = (stageVars) ? stageVars.VERIFICATION_TOKEN : undefined ; - let c_id = (stageVars) ? stageVars.CLIENT_ID : undefined ; - let c_secret = (stageVars) ? stageVars.CLIENT_SECRET : undefined ; - env = (stageVars) ? stageVars.ENV : "PROD" ; - if(log) console.log('NERDJOKES - Received Context:', JSON.stringify(context)); - let event ; + if(log) console.log("NERDJOKES Start - Data: " , data); let result ; - let team_id ; - - if (data.httpMethod == "POST"){ - if(log) console.log("NERDJOKES - RECEIVED POST"); - if(data.headers['Content-Type'] == "application/json"){ - if(log) console.log("NERDJOKES - RECEIVED POST - JSON"); - event = (typeof data.body == "string") ? JSON.parse(data.body) : data.body ; - } else if(data.headers['Content-Type'] == 'application/x-www-form-urlencoded' ){ - if(log) console.log("NERDJOKES - RECEIVED POST - URLENCODED"); - event = slacktools.querystringToJSON(data.body); - if(event.payload) { - if(log) console.log("NERDJOKES - RECEIVED PAYLOAD : ", JSON.stringify(event)); - event = JSON.parse(event.payload); - } - } - } else if (data.httpMethod == "GET"){ - if(log) console.log("NERDJOKES - RECEIVED GET"); - if(data.queryStringParameters){ - // ==#####== OAUTH ==#####== - event = data.queryStringParameters; - if (log) console.log("NERDJOKES - Event Code : ", event.code); - } else if(data.body == null && data.queryStringParameters == null) { - // ==#####== DIRECT INSTALL FROM APP DIRECTORY ==##### - if(log) console.log("NERDJOKES - DIRECT INSTALL FROM APP DIRECTORY "); - var url = "https://slack.com/oauth/v2/authorize" ; - var qs = "client_id=" + c_id + "&scope=chat:write,commands,im:write" ; - var msg = { - statusCode: 302, - headers: { "Location": url + "?" + qs }, - body: null - }; - if(log) console.log("NERDJOKES - DIRECT INSTALL FROM APP DIRECTORY - Message", msg); - return msg; - } + var event = new Event(data); + if(data.body == null && data.queryStringParameters == null) { + return event.directInstallRedirect(data); } - if(log) console.log('NERDJOKES - Received Event:', JSON.stringify(event)); - if (event.code) { // ==#####== OAUTH REQUEST - APP INSTALL ==#####== // ==#####== THIS IS A WEB BASED REQUEST ==#####== - result = await slacktools.oAuthVerify(event, { - client_id: c_id, - client_secret: c_secret, - env: env - }); + if(log) console.log("NERDJOKES - Event Code : " , event.code); + result = event.oAuthVerify(); return result; - } else if (event.hasOwnProperty("team_id") || event.hasOwnProperty("team")) { - // ==#####== CALCULATE TEAM ID ==#####== - team_id = slacktools.getTeamId(event, env) ; - if(log) console.log("NERDJOKES - Team ID : ", team_id); - // ==#####== GET AUTH TOKEN ==#####== - b_token = await nerdhelpers.getAccessToken({ team_id: team_id }); + } else if(event.command && event.text && event.text == "getjokejson"){ + // ==#####== SKIP OVER ACCESS TOKEN ==#####== + // ==#####== THIS IS FOR NERDJOKES ALEXA SKILL ==#####== + } else if(event.team_id){ + if(log) console.log("NERDJOKES - Getting Access Token"); + event.b_token = await event.getAccessToken(); + if(log) console.log("NERDJOKES - Got Access Token : " , event.b_token); } - - - + + if (event.hasOwnProperty("type") || event.hasOwnProperty("event")) { let event_type; - if(event.hasOwnProperty("event")) { event_type = event.event.type; } else if (event.hasOwnProperty("type")) { event_type = event.type; } + if(event.hasOwnProperty("event")) { event_type = event.event.type; } + else if (event.hasOwnProperty("type")) { event_type = event.type; } if(log) console.log("NERDJOKES - Event Type: " , event_type); switch (event_type) { case "url_verification": // ==#####== VERIFY EVENT CALLBACK URL ==#####== if(log) console.log("NERDJOKES - URL Verification"); - result = slacktools.urlVerify(event.challenge, event.token, v_token); + result = event.verifyURL(); break; case "app_uninstalled": // ==#####== APP UNINSTALLED _ DELETE ALL DATA FOR THAT WORKSPACE ==#####== - if(log) console.log("NERDJOKES- App Uninstalled"); - team_id = slacktools.getTeamId(event, env); - let cleaned = await nerdhelpers.cleanUninstall({ - team_id: team_id - }); + if(log) console.log("NERDJOKES - App Uninstalled"); + await event.cleanUninstall(); // ==#####== LOG EVENT ==#####== - var logged = await slacktools.logEvent(event, { team_id: team_id }); + await event.logEvent(); break; case "view_submission": // ==#####== DIALOG BOX SUBMITTED ==#####== if(log) console.log("NERDJOKES - View Submission"); + var schedule ; if(event.view.callback_id == 'newjoke-submit'){ if(log) console.log("NERDJOKES - New Joke Submitted"); - var res = await nerdhelpers.addNewJokeSubmit(event); - var res = await nerdhelpers.addNewJokeThankYou(event, { b_token: b_token }); + var joke = new Joke(); + await joke.addNewJokeSubmit(event); } else if(event.view.callback_id == "newjokeschedule-submit"){ if(log) console.log("NERDJOKES - New Joke Schedule Submitted"); - var res = await nerdhelpers.addNewScheduleSubmit(event); - var res = await nerdhelpers.addNewScheduleThankYou(event, { b_token: b_token }); + schedule = new Schedule(); + await schedule.addNewScheduleSubmit(event); } else if(event.view.callback_id == "deleteschedule-submit"){ if(log) console.log("NERDJOKES - Delete Schedule Submitted"); - var res = await nerdhelpers.deleteScheduleSubmit(event); - var res = await nerdhelpers.deleteScheduleThankYou(event, { b_token: b_token }); + schedule = new Schedule(); + await schedule.deleteScheduleSubmit(event); } // ==#####== LOG EVENT ==#####== - var logged = await slacktools.logEvent(event, { team_id: team_id }); + await event.logEvent(); // ==#####== NULL SUBMIT CLOSES VIEW ==#####== result = null; break; @@ -126,7 +79,6 @@ exports.handler = async function(data, context) { if(log) console.log("NERDJOKES - View Closed"); break; case "interactive_message": - team_id = slacktools.getTeamId(event, env); if(log) console.log("NERDJOKES - Interactive Message"); // ==#####== INTERACTIVE MESSAGES ==#####== // INTERACTIVE MESSAGES REPLACED WITH BLOCK MESSAGES @@ -137,44 +89,40 @@ exports.handler = async function(data, context) { } - // ==#####== SLASH COMMANDS ==#####== if(event.command) { if(log) console.log("NERDJOKES - Slash Command"); - team_id = slacktools.getTeamId(event, env); - if((event.command == "/nerdjokes") || (event.command == "/stkrdev")){ + if((event.command == "/nerdjokes") || (event.command == "/nerdjokesdev")){ if(event.text) { // ==#####== SLASH COMMAND WITH TEXT ==#####== - const slashData = { - b_token: b_token, - team_id: team_id, - env: env - }; - result = await nerdhelpers.processSlashCommand(event, slashData); + result = await event.processSlashCommand(); } else if (event.text.length == 0) { // ==#####== SEND RANDOM JOKE ==#####== - let sent = await nerdhelpers.sendJoke(event,{b_token: b_token}); + event.text = "getjoke"; + result = await event.processSlashCommand(); } // ==#####== LOG EVENT ==#####== - var logged = await slacktools.logEvent(event, { team_id: team_id }); + await event.logEvent(); } } - if(log) console.log('NERDJOKES END - Response Results:', result); let finalMsg = { isBase64Encoded: false, statusCode: 200, headers: { "Content-type": "application/json; charset=utf-8", - "Authorization": "Bearer " + b_token + "Authorization": "Bearer " + event.b_token } }; + if(log) console.log("NERDJOKES END - typeof result : ", typeof result); if(result == null){ } else if(typeof result === "object") { - if(result.hasOwnProperty("challenge")){ + try{ finalMsg.body = JSON.stringify( result ); + } catch(error) { + finalMsg.body = result ; } } else if( (result) && (result.length > 0) ){ finalMsg.body = JSON.stringify({ @@ -186,4 +134,5 @@ exports.handler = async function(data, context) { } if(log) console.log('NERDJOKES END - Final Message :', finalMsg); return finalMsg; + }; \ No newline at end of file diff --git a/joke.js b/joke.js new file mode 100644 index 0000000..e741d8b --- /dev/null +++ b/joke.js @@ -0,0 +1,352 @@ +console.log('Loading Joke class'); + +const CRYPTO = require('crypto'); +var AWS = require("./aws"); +var Message = require("./message"); + +const log = true ; + +module.exports = class Joke { + + constructor(){ + if(log) console.log("JOKE CONSTRUCTOR"); + this.id ; + this.question ; + this.answer ; + this.raw ; + this.tags ; + this.email = "nerdjokes@pixelated.tech" ; + return this ; + } + + async load(){ + if(log) console.log("JOKE LOAD"); + let rnd = this.getGUID(); + if(log) console.log("JOKE LOAD - GUID : ", rnd); + var ddbc = new AWS.DDBCLIENT(); + let myjoke = await ddbc.scanFromDynamo({ + tablename: ddbc.jokesDB, + start_key: { "id": rnd } , + limit: 1 + }); + if(log) console.log("JOKE LOAD - Joke : ", myjoke); + this.raw = myjoke.Items[0] ; + this.id = myjoke.Items[0].id ; + this.question = myjoke.Items[0].question ; + this.answer = myjoke.Items[0].answer ; + return; + } + + +// ========================================== +// SHARED FUNCTIONS +// ========================================== + + + sortByProperty(property){ + // MUST RUN AGAINST AN ARRAY AS ARRAY SORT FUNCTION + // https://medium.com/@asadise/sorting-a-json-array-according-one-property-in-javascript-18b1d22cd9e9 + return function(a,b){ + if(a[property] > b[property]) + return 1; + else if(a[property] < b[property]) + return -1; + return 0; + }; + } + + + getRandomInt (min, max) { + var numbers = []; + numbers.length = 20; + for (var i = 0; i < numbers.length ; i++) { + numbers[i] = this.randomCryptoInt(min, max); + } + if(log) console.log("GET RANDOM INT - Min: " , min , " Max: ", max , " Nums: ", numbers); + return numbers[this.randomInt(0, numbers.length - 1)]; + } + getGUID(){ + console.log("GET GUID"); + let guid = this.randomGUIDv4(); + console.log("GET GUID - ", guid); + return guid ; + } + randomInt(min, max) { + // min and max are inclusive + min = Math.ceil(min); + max = Math.floor(max); + return Math.floor(Math.random() * (max - min + 1)) + min; + } + randomCryptoInt(min, max) { + min = Math.ceil(min); + max = Math.floor(max); + if(min >= max) return false; + const diff = max - min + 1; + const rnd = CRYPTO.randomBytes(8).readUInt32LE() / 0xffffffff ; + return Math.floor( rnd * diff ) + min ; + } + randomGUIDv1(){ + var hexString = CRYPTO.randomBytes(16).toString("hex"); + var guidString = hexString.substring(0,8) + "-" + + hexString.substring(8,12) + "-" + + hexString.substring(12,16) + "-" + + hexString.substring(16,20) + "-" + + hexString.substring(20); + return guidString; + } + randomGUIDv4(){ + if(log) console.log("RANDOM GUID v4 "); + let guid = ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c => + (c ^ (CRYPTO.randomBytes(1)[0] & (15 >> (c / 4)))).toString(16) + ); + if(log) console.log("RANDOM GUID v4 - ", guid); + return guid ; + } + randomStr(howMany, chars) { + chars = chars || 'abcdefghijklmnopqrstuwxyzABCDEFGHIJKLMNOPQRSTUWXYZ0123456789'; + var rnd = CRYPTO.randomBytes(howMany); + var value = new Array(howMany); + var len = Math.min(256, chars.length); + var d = 256 / len ; + for (var i = 0; i < howMany; i++) { + value[i] = chars[Math.floor(rnd[i] / d)]; + } + return value.join(''); + } + + +// ========================================== +// ADD JOKE FUNCTIONS +// ========================================== + + + async addNewJoke(event){ + if(log) console.log("ADD NEW JOKE "); + let message_data = await this.addNewJokeMessage(event); + if(log) console.log("ADD NEW JOKE - Message : ", message_data); + var message = new Message(message_data); + await message.slackApiPost(); + return null; + } + + + async addNewJokeMessage(event){ + if(log) console.log("ADD NEW JOKE MESSAGE "); + var tag_options = await this.getTagOptions(event); + let _view = JSON.stringify({ + "type": "modal", + "callback_id": "newjoke-submit", + "private_metadata": JSON.stringify({channel_id: event.channel_id}), + "clear_on_close": true, + "notify_on_close": false, + "title": { + "type": "plain_text", + "text": "NerdJokes Add New Joke" + }, + "submit": { + "type": "plain_text", + "text": "Submit" + }, + "close": { + "type": "plain_text", + "text": "Cancel" + }, + "blocks": [ + { + "type": "section", + "block_id": "newjoke_view_title", + "text": { + "type": "mrkdwn", + "text": "Please enter the details of your new joke." + } + }, + { + "type": "input", + "block_id": "newjoke_question_block", + "element": { + "type": "plain_text_input", + "multiline": true, + "action_id": "newjoke_question" + }, + "label": { + "type": "plain_text", + "text": "Question:" + } + }, + { + "type": "input", + "block_id": "newjoke_answer_block", + "element": { + "type": "plain_text_input", + "multiline": true, + "action_id": "newjoke_answer" + }, + "label": { + "type": "plain_text", + "text": "Answer:" + } + },{ + "type": "input", + "block_id": "newjoke_tags_block", + "label": { + "type": "plain_text", + "text": "Tags: (select all that apply)" + }, + "element": { + "type": "multi_static_select", + "action_id": "newjoke_tags_selected", + "placeholder": { + "type": "plain_text", + "text": "Select tags..." + }, + // "initial_option": { }, + "options": tag_options + } + } + ] + }); + if(log) console.log("ADD NEW JOKE MESSAGE - View : ", _view); + let message = { + trigger_id: event.trigger_id, + view: _view, + api_method: "views.open", + b_token: event.b_token + }; + if(log) console.log("ADD NEW JOKE MESSAGE - Message : ", message); + return(message); + } + + + async getTagOptions(event){ + if(log) console.log("GET TAG OPTIONS "); + var ddbc = new AWS.DDBCLIENT(); + let mytags = await ddbc.scanFromDynamo({ + tablename: ddbc.jokesTagsDB + }); + if(log) console.log("GET TAG OPTIONS - Tags : ", mytags); + // ==#####== SORT ITEMS ==#####== + mytags.Items = mytags.Items.sort(this.sortByProperty("tag")); + if(log) console.log("GET TAG OPTIONS - Tags Sorted : ", mytags); + var items = []; + for (var i = 0; i < mytags.Items.length; i++) { + var item = {}; + item.text = { "type": "plain_text", "text": mytags.Items[i].tag } ; + item.value = mytags.Items[i].tag ; + items.push(item); + } + if(log) console.log("GET TAG OPTIONS : ", items); + return items; + } + + + async addNewJokeSubmit(event){ + if(log) console.log("ADD NEW JOKE SUBMIT "); + // var prvmeta = JSON.parse(event.view.private_metadata); + var q = event.view.state.values.newjoke_question_block.newjoke_question.value.replace(/\+/g, ' ') ; + var a = event.view.state.values.newjoke_answer_block.newjoke_answer.value.replace(/\+/g, ' ') ; + var tags = event.view.state.values.newjoke_tags_block.newjoke_tags_selected.selected_options ; // array + var tags_set = []; + tags.forEach(async function(tag) { + tags_set.push(tag.value); + }); + if(event.team.id == "T011Q2H2HQ8" && event.user.id == "U011Q2H2KG8"){ + var ddbc = new AWS.DDBCLIENT(); + await ddbc.writeToDynamo({ + tablename: ddbc.jokesDB, + item: { + id : this.getGUID() ,// Date.now().valueOf().toString() , + question : q , + answer : a , + tags : await ddbc.createDynamoSet(tags_set) , + put_date : new Date().toISOString() + } + }); + if(log) console.log("ADD NEW JOKE SUBMIT - Submitted "); + } else { + var ses = new AWS.SES(); + await ses.sendEmail({ + to: this.email, + from: this.email, + subject: "New NerdJokes Submitted", + body: q + "\n\n" + a + }); + } + await this.addNewJokeThankYou(event); + return null; + } + + + async addNewJokeThankYou(event){ + if(log) console.log("ADD NEW JOKE THANK YOU "); + var prvmeta = JSON.parse(event.view.private_metadata); + let message_data = { + token: event.b_token, + replace_original: true, + channel: prvmeta.channel_id, + response_type: 'ephemeral' , + text: "Thank you for submitting a new NerdJoke!", + b_token: event.b_token , + api_method: "chat.postMessage" , + response_url: event.response_url + }; + if(log) console.log("ADD NEW JOKE THANK YOU - Result : ", message_data); + var message = new Message(message_data); + await message.slackApiPost() ; + return null; + } + + +// ========================================== +// SEND JOKE FUNCTIONS +// ========================================== + + + async sendJoke(event){ + if(log) console.log("SEND JOKE " ); + // const joke = await this.getJoke(); + const sendquestion = await this.sendJokeQuestion(event); + await this.sendJokeAnswer(event, sendquestion.message.ts ); + return null; + } + + + // TODO: Check if private channel, and if app is a member, otherwise you get an error + + async sendJokeQuestion(event){ + if(log) console.log("SEND JOKE QUESTION "); + let message_data = { + token: event.b_token, + replace_original: true, + channel: event.channel_id, + response_type: 'in_channel' , + text: this.question , + b_token: event.b_token , + api_method: "chat.postMessage" , + }; + if(log) console.log("SEND JOKE QUESTION - Result : ", message_data); + var message = new Message(message_data); + var result = await message.slackApiPost() ; + return result; + } + + + async sendJokeAnswer(event, q_ts){ + if(log) console.log("SEND JOKE ANSWER - Even "); + if(log) console.log("SEND JOKE ANSWER - Q TS : ", q_ts); + let message_data = { + token: event.b_token, + replace_original: true, + channel: event.channel_id, + response_type: 'in_channel' , + text: this.answer , + b_token: event.b_token , + api_method: "chat.postMessage" , + thread_ts: q_ts + }; + if(log) console.log("SEND JOKE ANSWER - Result : ", message_data); + var message = new Message(message_data); + await message.slackApiPost() ; + return null; + } + +}; \ No newline at end of file diff --git a/message.js b/message.js new file mode 100644 index 0000000..588abd4 --- /dev/null +++ b/message.js @@ -0,0 +1,139 @@ +console.log('Loading Message class'); + +const HTTPS = require('https'); +const QS = require('querystring'); + +const log = true ; + +module.exports = class Message { + + constructor(props){ + this.message = {}; + if(log) console.log("MESSAGE CONSTRUCTOR"); + if(log) console.log("MESSAGE CONSTRUCTOR props : ", props); + (props.token) ? this.token = props.token : null ; + (props.client_id) ? this.message.client_id = props.client_id : null ; + (props.client_secret) ? this.message.client_secret = props.client_secret : null ; + (props.code) ? this.message.code = props.code : null ; + + (props.delete_original) ? this.message.delete_original = props.delete_original : this.message.delete_original = false ; + (props.replace_original) ? this.message.replace_original = props.replace_original : this.message.replace_original = false ; + (props.response_type) ? this.message.response_type = props.response_type : this.message.response_type = 'in_channel' ; // 'ephemeral' + (props.thread_ts) ? this.message.thread_ts = props.thread_ts : null ; + (props.return_im) ? this.message.return_im = props.return_im : null ; + (props.trigger_id) ? this.message.trigger_id = props.trigger_id : null ; + (props.channel) ? this.message.channel = props.channel : null ; + (props.users) ? this.message.users = props.users : null ; + + (props.text) ? this.message.text = props.text : null ; + (props.blocks) ? this.message.blocks = props.blocks : null ; + (props.view) ? this.message.view = props.view : null ; + if(log) console.log("MESSAGE CONSTRUCTOR - This Message : ", this.message); + + this.option_data = {}; + (props.b_token) ? this.option_data.b_token = props.b_token : null ; + (props.api_method) ? this.option_data.api_method = props.api_method : null ; + (props.response_url) ? this.option_data.response_url = props.response_url : null ; + (props.api_method) ? this.option_data.api_method = props.api_method : null ; + if((props.response_url) && (props.response_url.length > 0)){ + this.option_data.url = props.response_url; + } else if(props.api_method) { + this.option_data.url = "https://slack.com/api/" + props.api_method; + } + if(log) console.log("MESSAGE CONSTRUCTOR - This Option Data : ", this.option_data); + return this ; + } + + + async slackApiPost(){ + // ==#####== SLACK API POST ==#####=== + if(log) console.log("SLACK API POST "); + var options = { + method: "POST", + port: 443, + headers: { + "Authorization": "Bearer " + this.option_data.b_token, + "Content-Type": "application/json; charset=utf-8", + "Content-Length": JSON.stringify(this.message).length + } + }; + if(log) console.log("SLACK API POST - Request Options : ", options); + let retJSON = ''; + return new Promise( (resolve, reject) => { + const request = HTTPS.request(this.option_data.url, options, function(res) { + if(log) console.log('SLACK API POST - Request Response', res); + if(log) console.log('SLACK API POST - Request Response Status Code:', res.statusCode); + if(log) console.log('SLACK API POST - Request Response Headers:', res.headers); + res.setEncoding( 'utf8' ); + res.on("data", function(chunk){ /* console.log("Chunk = ", chunk); */ retJSON += chunk; }); + res.on("end", function () { + if (typeof(retJSON) === "string") { + try { + // ==#####== STRINGIFIED RESPONSE ==#####== + if(log) console.log("SLACK API POST - Response Results", retJSON); + resolve(JSON.parse(retJSON)) ; + } catch(err) { + // ==#####== NATIVE JSON RESPONSE ==#####== + if(log) console.log("SLACK API POST - Response Results", JSON.stringify(retJSON)); + resolve(retJSON) ; + } + } else { + resolve(retJSON) ; + } + }); + }); + request.on("error", function (error) { + console.log("SLACK API POST ERROR : ", JSON.stringify(error)); + console.log("SLACK API POST ERROR Code : ", JSON.stringify(error.code)); + console.log("SLACK API POST ERROR Message : ", JSON.stringify(error.message)); + console.log("SLACK API POST ERROR Stack : ", JSON.stringify(error.stack)); + reject(error); + }); + request.write(JSON.stringify(this.message)); + request.end(); + if(log) console.log("SLACK API POST - Request Results : ", request); + }); + } + + + async slackApiGet(){ + // ==#####== SLACK API GET ==#####== + if(log) console.log("SLACK API GET - Message : ", this.message); + var qstring = QS.stringify(this.message); + if(log) console.log("SLACK API GET - Request QString : ", qstring); + var options = { + method: "GET", + port: 443, + protocol: "https:", + hostname: "slack.com", + path: "/api/" + this.option_data.api_method + "?" + qstring + }; + if(log) console.log("SLACK API GET - Request File Options : ", options); + let retJSON = ''; + return new Promise( (resolve, reject) => { + const request = HTTPS.request(options, function(res) { + if(log) console.log('SLACK API GET - Request Status Code:', res.statusCode); + if(log) console.log('SLACK API GET - Request Headers:', res.headers); + res.setEncoding( 'utf8' ); + res.on("data", function(data) { + retJSON += data; + }); + res.on("end", function () { + if(log) console.log("SLACK API GET - Response Results", retJSON.toString()); + resolve(JSON.parse(retJSON)); + }); + }); + request.on("error", function (error) { + console.log("SLACK API GET ERROR : ", JSON.stringify(error)); + console.log("SLACK API GET ERROR Code : ", JSON.stringify(error.code)); + console.log("SLACK API GET ERROR Message : ", JSON.stringify(error.message)); + console.log("SLACK API GET ERROR Stack : ", JSON.stringify(error.stack)); + reject(error); + }); + request.end(); + if(log) console.log("SLACK API GET - Request Results : ", request); + }); + } + + +}; \ No newline at end of file diff --git a/nerdjokes.yaml b/nerdjokes.yaml index 39fc562..c562252 100644 --- a/nerdjokes.yaml +++ b/nerdjokes.yaml @@ -10,9 +10,13 @@ Resources: CodeUri: . Description: '' MemorySize: 256 - Timeout: 6 + Timeout: 30 Role: 'arn:aws:iam::971579260814:role/tech-jokes-role' Events: + Schedule1: + Type: Schedule + Properties: + Schedule: cron(0 0-23 * * ? *) Api1: Type: Api Properties: diff --git a/schedule.js b/schedule.js new file mode 100644 index 0000000..623818b --- /dev/null +++ b/schedule.js @@ -0,0 +1,486 @@ +console.log('Loading Schedule class'); + +var AWS = require("./aws"); +var Message = require("./message"); + +const log = true ; + +module.exports = class Schedule { + + constructor(){ + if(log) console.log("SCHEDULE CONSTRUCTOR"); + // this.team_id ; + // this.team_domain ; + // this.channel_id ; + this.days ; + this.hour ; + this.gmt_hour ; + this.timezone ; + return this ; + } + + +// ========================================== +// ADD SCHEDULE FUNCTIONS +// ========================================== + + + async addNewSchedule(event){ + if(log) console.log("ADD NEW SCHEDULE "); + let message_data = await this.addNewScheduleMessage(event); + if(log) console.log("ADD NEW SCHEDULE - Message : ", message_data); + var message = new Message(message_data); + let res = await message.slackApiPost(); + if(log) console.log("ADD NEW SCHEDULE - Response : ", res); + return res; + } + + + async addNewScheduleMessage(event, data){ + if(log) console.log("ADD NEW SCHEDULE MESSAGE "); + let tz_options = await this.getTimeZoneOptions(); + let day_options = await this.getDayOptions(); + let hour_options = await this.getHourOptions(); + let _view = { + "type": "modal", + "callback_id": "newjokeschedule-submit", + "private_metadata": JSON.stringify({channel_id: event.channel_id}), + "clear_on_close": true, + "notify_on_close": false, + "title": { "type": "plain_text", "text": "NerdJokes Add Schedule" }, + "submit": { "type": "plain_text", "text": "Submit" }, + "close": { "type": "plain_text", "text": "Cancel" }, + "blocks": [ + { + "type": "section", + "block_id": "newjokeschedule_title", + "text": { + "type": "mrkdwn", + "text": "Please Add, Edit, or Delete your schedule:" + } + },{ + "type": "section", + "block_id": "newjokeschedule_fields", + "fields": [ + { "type": "mrkdwn", "text": "*Team ID:* \n " + event.team_id + " \n " + event.team_domain }, + { "type": "mrkdwn", "text": "*Channel ID:* \n " + event.channel_id } + ] + },{ + "type": "input", + "block_id": "newjokeschedule_days", + "label": { + "type": "plain_text", + "text": "Scheduled Days: (select one or more)" + }, + "element": { + "type": "multi_static_select", + "action_id": "newjokeschedule_days_selected", + "placeholder": { + "type": "plain_text", + "text": "Select schedule days..." + }, + // "initial_options": [], + "options": day_options + } + },{ + "type": "input", + "block_id": "newjokeschedule_time", + "label": { + "type": "plain_text", + "text": "Scheduled Time: (select one)" + }, + "element": { + "type": "static_select", + "action_id": "newjokeschedule_time_selected", + "placeholder": { + "type": "plain_text", + "text": "Select schedule time..." + }, + // "initial_option": { }, + "options": hour_options + } + },{ + "type": "input", + "block_id": "newjokeschedule_timezone", + "label": { + "type": "plain_text", + "text": "Scheduled Time Zone: (select one)" + }, + "element": { + "type": "static_select", + "action_id": "newjokeschedule_timezone_selected", + "placeholder": { + "type": "plain_text", + "text": "Select schedule time zone..." + }, + // "initial_option": { }, + "options": tz_options + } + } + ] + }; + // ==#####== GET INITIAL SCHEDULE ==#####== + + var ddbc = new AWS.DDBCLIENT(); + let schedules = await ddbc.readFromDynamo({ + tablename: ddbc.jokesScheduleDB, + key_cond_expr: "team_id = :teamid AND channel_id = :channelid", + attrib_vals: { ":teamid": event.team_id , ":channelid": event.channel_id } + }); + if(log) console.log("ADD NEW SCHEDULE MESSAGE - Schedules : ", schedules); + if(schedules.Items.length > 0) { + var schedule = schedules.Items[0]; + if(log) console.log("ADD NEW SCHEDULE MESSAGE - Schedule : ", schedule); + // ==#####== SET DAYS ==#####== + _view.blocks[2].element.initial_options = []; + schedule.days.values.forEach(async function(day) { + _view.blocks[2].element.initial_options.push(_view.blocks[2].element.options[day]); + }); + // ==#####== SET TIME ==#####== + _view.blocks[3].element.initial_option = _view.blocks[3].element.options[parseInt(schedule.hour, 10)]; + // ==#####== SET TIMEZONE ==#####== + _view.blocks[4].element.initial_option = _view.blocks[4].element.options.find(tz => tz.value === schedule.timezone); + } + if(log) console.log("ADD NEW SCHEDULE MESSAGE - View : ", JSON.stringify(_view)); + let message = { + trigger_id: event.trigger_id, + view: JSON.stringify(_view), + api_method: "views.open", + b_token: event.b_token + }; + if(log) console.log("ADD NEW SCHEDULE MESSAGE - Message : ", message); + return(message); + } + + + async getTimeZoneOptions(){ + if(log) console.log("GET TIMEZONE OPTIONS "); + // https://gist.github.com/jaredreich/9f4aa2a1d460100aa40ddbc279d85660#file-timezones-json + var timezones = require("./timezones.json"); + var items = []; + for (var i = 0; i < timezones.length; i++) { + var item = {}; + item.text = { "type": "plain_text", "text": timezones[i].text } ; + item.value = timezones[i].value.toString(); + items.push(item); + } + if(log) console.log("GET TIME ZONE OPTIONS : ", items); + return items; + } + + + async getDayOptions(){ + if(log) console.log("GET DAY OPTIONS "); + var days = new Array( "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ); + var items = []; + for (var i = 0; i < days.length; i++) { + var item = {}; + item.text = { "type": "plain_text", "text": days[i] } ; + item.value = i.toString(); + items.push(item); + } + if(log) console.log("GET DAY OPTIONS : ", items); + return items; + } + + + async getHourOptions(){ + if(log) console.log("GET HOUR OPTIONS "); + var items = []; + for (var i = 0; i < 24; i++) { + var item = {}; + var hour = (i > 12) ? (i - 12 + ":00pm") : (((i==0) ? 12 : i ) + ":00am"); + item.text = { "type": "plain_text", "text": hour } ; + item.value = i.toString(); + items.push(item); + } + if(log) console.log("GET HOUR OPTIONS : ", items); + return items; + } + + + async addNewScheduleSubmit(event){ + if(log) console.log("ADD NEW SCHEDULE SUBMIT - New Schedule Submitted "); + + var _channel_id = event.view.blocks[1].fields[1].text; + _channel_id = _channel_id.substr( _channel_id.lastIndexOf("+") + 1 , _channel_id.length ); + if(log) console.log("ADD NEW SCHEDULE SUBMIT - New Joke Schedule Submitted - Channel ID : ", _channel_id); + + var days = event.view.state.values.newjokeschedule_days.newjokeschedule_days_selected.selected_options ; // array + var days_set = []; + days.forEach(async function(day) { + days_set.push(parseInt(day.value, 10)); + }); + if(log) console.log("ADD NEW SCHEDULE SUBMIT - New Joke Schedule Submitted - Days : ", days_set.toString()); + var hour = event.view.state.values.newjokeschedule_time.newjokeschedule_time_selected.selected_option.value ; // str to num + var tz = event.view.state.values.newjokeschedule_timezone.newjokeschedule_timezone_selected.selected_option.value ; // str to num + var _gmt_hour = parseInt(hour, 10) - ( parseInt(tz, 10) ) ; + if (_gmt_hour > 24) _gmt_hour = _gmt_hour - 24 ; + + var ddbc = new AWS.DDBCLIENT(); + await ddbc.writeToDynamo({ + tablename: ddbc.jokesScheduleDB, + item: { + team_id : event.team_id , + channel_id : _channel_id , + days : await ddbc.createDynamoSet(days_set) , + hour : parseInt(hour, 10) , + timezone : tz , + gmt_hour : _gmt_hour , + put_date : new Date().toISOString() + } + }); + await this.addNewScheduleThankYou(event); + return null; + } + + + async addNewScheduleThankYou(event){ + if(log) console.log("ADD NEW SCHEDULE THANK YOU - Event "); + var prvmeta = JSON.parse(event.view.private_metadata); + let message_data = { + token: event.b_token, + replace_original: true, + channel: prvmeta.channel_id, + response_type: 'ephemeral' , + text: "The joke schedule for this channel has been successfully created." , + b_token: event.b_token , + api_method: "chat.postMessage" , + response_url: event.response_url + }; + if(log) console.log("ADD NEW SCHEDULE THANK YOU - Result : ", message_data); + var message = new Message(message_data); + await message.slackApiPost(); + return null; + } + + +// ========================================== +// DELETE SCHEDULE FUNCTIONS +// ========================================== + + + async getSchedule(event){ + if(log) console.log("GET SCHEDULE"); + var ddbc = new AWS.DDBCLIENT(); + let schedule = await ddbc.readFromDynamo({ + tablename: ddbc.jokesScheduleDB, + key_cond_expr: "team_id = :teamid AND channel_id = :channelid" , + attrib_vals: { ":teamid": event.team_id , ":channelid": event.channel_id } + }); + if(log) console.log("GET SCHEDULE - Schedule : ", schedule); + if(schedule.Items && schedule.Items.length > 0) { + // this.team_id = event.team_id; + // this.team_domain = event.team_domain; + // this.channel_id = event.channel_id; + this.days = schedule.Items[0].days.values.toString() ; + this.hour = schedule.Items[0].hour ; + this.timezone = schedule.Items[0].timezone ; + return true; + } + return false ; + } + + + async deleteSchedule(event){ + if(log) console.log("DELETE SCHEDULE "); + let message_data; + let postData; + var hasSchedule = await this.getSchedule(event); + if(hasSchedule) { + message_data = await this.deleteScheduleMessage(event); + if(log) console.log("DELETE SCHEDULE - Message : ", message_data); + } else { + message_data = await this.noScheduleMessage(event); + if(log) console.log("DELETE SCHEDULE - Message : ", message_data); + } + if(log) console.log("DELETE SCHEDULE - Post Data : ", postData); + var message = new Message(message_data); + await message.slackApiPost(); + return null; + } + + + async deleteScheduleMessage(event){ + if(log) console.log("DELETE SCHEDULE MESSAGE "); + let _view = JSON.stringify({ + "type": "modal", + "callback_id": "deleteschedule-submit", + "private_metadata": JSON.stringify({channel_id: event.channel_id}), + "clear_on_close": true, + "notify_on_close": false, + "title": { + "type": "plain_text", + "text": "NerdJokes DeleteSchedule" + }, + "submit": { + "type": "plain_text", + "text": "Submit" + }, + "close": { + "type": "plain_text", + "text": "Cancel" + }, + "blocks": [ + { + "type": "section", + "block_id": "deleteschedule_title", + "text": { + "type": "plain_text", + "text": "Are you sure you want to delete the schedule for this workspace and channel?" + } + },{ + "type": "section", + "block_id": "deleteschedule_fields", + "fields": [ + { "type": "mrkdwn", "text": "*Team ID:* \n " + event.team_id + " \n " + event.team_domain }, + { "type": "mrkdwn", "text": "*Channel ID:* \n " + event.channel_id }, + { "type": "mrkdwn", "text": "*Days:* \n " + this.days }, + { "type": "mrkdwn", "text": "*Hour:* \n " + this.hour }, + { "type": "mrkdwn", "text": "*Timezone:* \n " + this.timezone } + ] + },{ + "type": "input", + "block_id": "deleteschedule_confirm", + "label": { + "type": "plain_text", + "text": "Confirm Delete" + }, + "element": { + "type": "checkboxes", + "action_id": "deleteschedule_selected", + "options": [ + { + "text": { + "type": "plain_text", + "text": "Yes, please delete this schedule" + }, + "value": "schedule_delete" + } + ] + } + } + ] + }); + if(log) console.log("DELETE SCHEDULE - View : ", _view); + let message = { + trigger_id: event.trigger_id, + view: _view, + api_method: "views.open", + b_token: event.b_token + }; + if(log) console.log("DELETE SCHEDULE - Message : ", message); + return(message); + } + + + async noScheduleMessage(event){ + if(log) console.log("NO SCHEDULE MESSAGE "); + let message = { + token: event.b_token, + replace_original: true, + channel: event.channel_id, + response_type: 'ephemeral' , + text: "There is no schedule for this channel to be deleted.", + b_token: event.b_token , + api_method: "chat.postMessage" , + response_url: event.response_url + }; + if(log) console.log("NO SCHEDULE MESSAGE - Result : ", message); + return (message); + } + + + async deleteScheduleSubmit(event){ + if(log) console.log("DELETE SCHEDULE SUBMIT "); + var _channel_id = event.view.blocks[1].fields[1].text; + _channel_id = _channel_id.substr( _channel_id.lastIndexOf("+") + 1 , _channel_id.length ); + if(log) console.log("DELETE SCHEDULE SUBMIT - Submitted - Channel ID : ", _channel_id); + var del = event.view.state.values.deleteschedule_confirm.deleteschedule_selected.selected_options ; // str to num + if(del[0].value && del[0].value == "schedule_delete"){ + var ddbc = new AWS.DDBCLIENT(); + await ddbc.deleteFromDynamo({ + tablename: ddbc.jokesScheduleDB, + key: { team_id : event.team_id , channel_id: _channel_id }, + }); + } + await this.deleteScheduleThankYou(event); + return null; + } + + + async deleteScheduleThankYou(event){ + if(log) console.log("DELETE SCHEDULE THANK YOU - Event "); + var prvmeta = JSON.parse(event.view.private_metadata); + let message_data = { + token: event.b_token, + replace_original: true, + channel: prvmeta.channel_id, + response_type: 'ephemeral' , + text: "The joke schedule for this channel has been successfully deleted.", + b_token: event.b_token , + api_method: "chat.postMessage" , + response_url: event.response_url + }; + if(log) console.log("DELETE SCHEDULE THANK YOU - Result : ", message_data); + + var message = new Message(message_data); + await message.slackApiPost(); + + return null; + } + + +// ========================================== +// RUN SCHEDULE FUNCTIONS +// ========================================== + + + async runSchedule(event){ + if(log) console.log("RUN SCHEDULE "); + var ddbc = new AWS.DDBCLIENT(); + let schedules = await ddbc.readFromDynamo({ + tablename: ddbc.jokesScheduleDB, + indexname: "gmt_hour-index", + key_cond_expr: "gmt_hour = :hour", + filter_expr: "contains(days, :day)", + attrib_vals: { ":hour": this.gmt_hour , ":day": this.day } + }); + if(log) console.log("RUN SCHEDULE - Schedules : ", schedules); + if(schedules.Items.length == 0 ) { + return null ; + } + if(log) console.log("RUN SCHEDULE - Schedule Count : ", schedules.Items.length); + for (var myschedule of schedules.Items){ + if(log) console.log("RUN SCHEDULE - My Schedule : ", myschedule); + // ==#####== GET CUSTOM TOKEN PER WORKSPACE ==#####== + myschedule.b_token = await event.getAccessToken({ team_id: myschedule.team_id }); + var lambdaPayload = { + "httpMethod": "POST", + "headers": { + "Content-Type": "application/json", + }, + "stageVariables":{ + "ENV": event.env + }, + "body": { + "token": myschedule.b_token, + "team_id": myschedule.team_id, + "channel_id": myschedule.channel_id, + "command": "/nerdjokes", + "text": "getjoke", + } + }; + if(log) console.log("RUN SCHEDULE - Recursive Lambda - Payload : ", lambdaPayload); + let lambdaParams = { + FunctionName: 'nerdjokes:' + event.env, + InvocationType: 'Event', + Payload: JSON.stringify(lambdaPayload) + }; + if(log) console.log("RUN SCHEDULE - Recursive Lambda - Params : ", lambdaParams); + var lambda = new AWS.LAMBDA(); + lambda.callLambda(lambdaParams); + } + } + +}; \ No newline at end of file From 662b605b9321a2eddf5b8c7ebe26b152a73ffb38 Mon Sep 17 00:00:00 2001 From: Brian Whaley Date: Thu, 28 May 2020 16:19:07 -0400 Subject: [PATCH 2/7] v3.0.0 v3.0.0 --- nerdjokes-helpers.js | 965 ------------------------------------------- 1 file changed, 965 deletions(-) delete mode 100644 nerdjokes-helpers.js diff --git a/nerdjokes-helpers.js b/nerdjokes-helpers.js deleted file mode 100644 index 97a241b..0000000 --- a/nerdjokes-helpers.js +++ /dev/null @@ -1,965 +0,0 @@ -console.log('Loading nerdjokes-helpers'); - -var awstools = require("./tools-aws"); -var slacktools = require("./tools-slack"); - -const email = "nerdjokes@pixelated.tech" ; -const email_link = `` ; -let log = true ; - - - - -exports.processSlashCommand = processSlashCommand; -async function processSlashCommand(event, data){ - if(log) console.log("PROCESS SLASH COMMAND - Event : ", event); - if(log) console.log("PROCESS SLASH COMMAND - Data : ", data); - if(log) console.log("PROCESS SLASH COMMAND - Text : ", event.text); - var cmd = event.command ; - let result ; - switch (event.text) { - case "help": - if(log) console.log("PROCESS SLASH COMMAND - Help"); - result = "* Type '"+cmd+"' to get a random joke to immediately share with your teammates. \n" + - "* Type '"+cmd+" bug' to get more information about submitting a bug. \n" + - "* Type '"+cmd+" support' to get more information on how to reach out for support. \n" + - "* Type '"+cmd+" getjoke' to get a random joke sent immediately. \n" + - "* Type '"+cmd+" addschedule' to add or edit a schedule for delivering jokes to the current channel. \n" + - "* Type '"+cmd+" deleteschedule' to delete a schedule for delivering jokes to the current channel. \n" + - "* Type '"+cmd+" addjoke' to recommend a new joke to be added to the collection. \n" ; - break; - case "bug": - if(log) console.log("PROCESS SLASH COMMAND - Bug"); - result = "If you want to report a bug, email " + email_link + " or join the bug channel on the pixelated-tech.slack.com workspace." ; - break; - case "support": - if(log) console.log("PROCESS SLASH COMMAND - Support"); - result = "If you need some support, email " + email_link + " or join the support channel on the pixelated-tech.slack.com workspace."; - break; - case "joke": - case "random": - case "getjoke": - // ==#####== SEND RANDOM JOKE ==#####== - let sent = await sendJoke(event,{b_token: data.b_token}); - result = null; - break; - case "addschedule": - if(log) console.log("PROCESS SLASH COMMAND - Add Schedule"); - var res = await addNewSchedule(event, data); - result = null; - break; - case "deleteschedule": - if(log) console.log("PROCESS SLASH COMMAND - Delete Schedule"); - var res = await deleteSchedule(event, data); - result = null; - break; - case "run": - // ==#####== USED BY THE SCHEDULED JOB TO SEND ALL JOKES TO ALL CHANNELS NAD ALL WORKSPACES ==#####== - if(log) console.log("PROCESS SLASH COMMAND - Run Schedule"); - var hours = new Date().getUTCHours(); - var day = new Date().getDay(); - if(log) console.log("PROCESS SLASH COMMAND - Run Schedule - d:", day, " , hr:", hours ); - var schedRun = await runSchedule({ - b_token: data.b_token, - gmt_hour: hours, - day: day - }); - break; - case "addjoke": - if(log) console.log("PROCESS SLASH COMMAND - Add"); - var res = await addNewJoke(event, data); - result = null; - break; - default: - if(log) console.log("PROCESS SLASH COMMAND - Default"); - result = "NerdJokes: Unknown Image or Command"; - } - return result; -} - - - - - - -// ========================================== -// ACCESS TOKEN FUNCTIONS -// ========================================== - - - - - - -exports.getAccessToken = getAccessToken; -async function getAccessToken(data){ - if(log) console.log("GET ACCESS TOKEN - data : ", data); - if(log) console.log("GET ACCESS TOKEN - Team ID : ", data.team_id); - let getAuth = await awstools.readFromDynamo({ - tablename: awstools.jokesTokenDB, - key_cond_expr: "team_id = :teamid", - attrib_vals: { ":teamid": data.team_id }, - proj_expr: "access_token" - }); - if(log) console.log("GET ACCESS TOKEN - Auth Results : ", getAuth); - if(log) console.log("GET ACCESS TOKEN - Access Token : ", await getAuth.Items[0].access_token); - let b_token = await getAuth.Items[0].access_token; - return b_token; -} - - - - - - -// ========================================== -// CLEAN UNINSTALL FUNCTIONS -// ========================================== - - - - - - - -exports.cleanUninstall = cleanUninstall; -async function cleanUninstall(data){ - if(log) console.log("CLEAN UNINSTALL - Data : ", data); - - // ==#####== GET ALL SCHEDULES ==#####== - const scheds = await awstools.readFromDynamo({ - tablename: awstools.jokesScheduleDB, - indexname: "team_id-index", - key_cond_expr: "team_id = :teamid", - attrib_vals: { ":teamid": data.team_id }, - proj_expr: "channel_id" - }); - if(log) console.log("CLEAN UNINSTALL - App Uninstalled - Scheds", scheds); - - // ==#####== DELETE SCHEDULES ==#####== - var itemsArray = []; - scheds.Items.forEach(async function(sched) { - var newitem = { - DeleteRequest : { - Key : { team_id: data.team_id, channel_id: sched.channel_id } - } - }; - itemsArray.push(newitem); - }); - if(log) console.log("CLEAN UNINSTALL - App Uninstalled - Items", itemsArray); - var params = { - RequestItems : { [awstools.jokesScheduleDB] : itemsArray } - }; - if(log) console.log("CLEAN UNINSTALL - App Uninstalled - Params", params); - const decheduled = await awstools.batchDeleteFromDynamo({ - params: params - }); - - // ==#####== DELETE TOKEN ==#####== - const detokened = await awstools.deleteFromDynamo( { - tablename: awstools.jokesTokenDB, - key: { "team_id" : data.team_id } - }); - if(log) console.log("CLEAN UNINSTALL - Complete"); -} - - - - -// ========================================== -// JOKE FUNCTIONS -// ========================================== - - - - -exports.sendJoke = sendJoke; -async function sendJoke(event, data){ - if(log) console.log("SEND JOKE - Event : ", event ); - if(log) console.log("SEND JOKE - Data : ", data ); - const joke = await getJoke(); - const sendquestion = await sendJokeQuestion(event, {b_token: data.b_token, joke: joke}); - const sendanswer = await sendJokeAnswer(event, {b_token: data.b_token, joke: joke, thread_ts: sendquestion.message.ts }); - return null; -} - - - -exports.getJoke = getJoke; -async function getJoke(){ - if(log) console.log("GET JOKE "); - const firstJoke = '1587787878120'; - const lastJoke = Date.now().valueOf().toString(); - let rnd = slacktools.getRandomInt(firstJoke, lastJoke); - if(log) console.log("GET JOKE - Rnd : ", rnd); - let joke = await awstools.scanFromDynamo({ - tablename: awstools.jokesDB, - start_key: { "id": rnd.toString() } , - limit: 1 - }); - if(log) console.log("GETJOKE - Joke : ", joke); - var result = { text: "Question: " + joke.Items[0].question + " \n " + "Answer: " + joke.Items[0].answer } ; - if(log) console.log("GETJOKE - Result : ", result); - return joke; -} - - - - -exports.sendJokeQuestion = sendJokeQuestion; -async function sendJokeQuestion(event, data){ - if(log) console.log("SEND JOKE QUESTION - Event : ", event); - if(log) console.log("SEND JOKE QUESTION - Data : ", data); - let message = { - token: data.b_token, - replace_original: true, - channel: event.channel_id, - response_type: 'in_channel' , - text: data.joke.Items[0].question , - b_token: data.b_token , - api_method: "chat.postMessage" , - }; - if(log) console.log("SEND JOKE QUESTION - Result : ", message); - let result = await slacktools.processApiMsg(message); - return result; -} - - - -exports.sendJokeAnswer = sendJokeAnswer; -async function sendJokeAnswer(event, data){ - if(log) console.log("SEND JOKE ANSWER - Event : ", event); - if(log) console.log("SEND JOKE ANSWER - Data : ", data); - let message = { - token: data.b_token, - replace_original: true, - channel: event.channel_id, - response_type: 'in_channel' , - text: data.joke.Items[0].answer , - b_token: data.b_token , - api_method: "chat.postMessage" , - thread_ts: data.thread_ts - }; - if(log) console.log("SEND JOKE ANSWER - Result : ", message); - let result = await slacktools.processApiMsg(message); - return null; -} - - - - -exports.addNewJoke = addNewJoke; -async function addNewJoke(event, data){ - if(log) console.log("ADD NEW JOKE - Data : ", data); - let msg = await addNewJokeMessage(event, data); - if(log) console.log("ADD NEW JOKE - Message : ", msg); - let postData = { - api_method: "views.open", - b_token: data.b_token - }; - if(log) console.log("ADD NEW JOKE - Post Data : ", postData); - let res = await slacktools.slackApiPost(msg, postData); - if(log) console.log("ADD NEW JOKE - Response : ", res); - return res; -} - - - - - -exports.addNewJokeMessage = addNewJokeMessage; -async function addNewJokeMessage(event, data){ - if(log) console.log("ADD NEW JOKE MESSAGE - Data : ", data); - let _view = JSON.stringify({ - "type": "modal", - "callback_id": "newjoke-submit", - "private_metadata": JSON.stringify({channel_id: event.channel_id}), - "clear_on_close": true, - "notify_on_close": false, - "title": { - "type": "plain_text", - "text": "NerdJokes Add New Joke" - }, - "submit": { - "type": "plain_text", - "text": "Submit" - }, - "close": { - "type": "plain_text", - "text": "Cancel" - }, - "blocks": [ - { - "type": "section", - "block_id": "newjoke_view_title", - "text": { - "type": "mrkdwn", - "text": "Please enter the details of your new joke." - } - }, - { - "type": "input", - "block_id": "newjoke_question_block", - "element": { - "type": "plain_text_input", - "multiline": true, - "action_id": "newjoke_question" - }, - "label": { - "type": "plain_text", - "text": "Question:" - } - }, - { - "type": "input", - "block_id": "newjoke_answer_block", - "element": { - "type": "plain_text_input", - "multiline": true, - "action_id": "newjoke_answer" - }, - "label": { - "type": "plain_text", - "text": "Answer:" - } - } - ] - }); - if(log) console.log("ADD NEW JOKE MESSAGE - View : ", _view); - let message = { - trigger_id: event.trigger_id, - view: _view - }; - if(log) console.log("ADD NEW JOKE MESSAGE - Message : ", message); - return(message); -} - - - - -exports.addNewJokeSubmit = addNewJokeSubmit; -async function addNewJokeSubmit(event){ - if(log) console.log("ADD NEW JOKE SUBMIT - Event : ", event); - var prvmeta = JSON.parse(event.view.private_metadata); - var q = event.view.state.values.newjoke_question_block.newjoke_question.value.replace(/\+/g, ' ') ; - var a = event.view.state.values.newjoke_answer_block.newjoke_answer.value.replace(/\+/g, ' ') ; - if(event.team.id == "T011Q2H2HQ8" && event.user.id == "U011Q2H2KG8"){ - let res = await awstools.writeToDynamo({ - tablename: awstools.jokesDB, - item: { - id : Date.now().valueOf().toString() , - question : q , - answer : a , - put_date : new Date().toISOString() - } - }); - if(log) console.log("ADD NEW JOKE SUBMIT - Submitted "); - } else { - const email_res = await awstools.sendEmail({ - subject: "New NerdJokes Submitted", - body: q + "\n\n" + a - }); - } -} - - - - - -exports.addNewJokeThankYou = addnewJokeThankYou; -async function addnewJokeThankYou(event, data){ - if(log) console.log("ADD NEW JOKE THANK YOU - Event : ", event); - if(log) console.log("ADD NEW JOKE THANK YOU - Data : ", data); - var prvmeta = JSON.parse(event.view.private_metadata); - let message = { - token: data.b_token, - replace_original: true, - channel: prvmeta.channel_id, - response_type: 'ephemeral' , - text: "Thank you for submitting a new NerdJoke!", - b_token: data.b_token , - api_method: "chat.postMessage" , - response_url: event.response_url - }; - if(log) console.log("ADD NEW JOKE THANK YOU - Result : ", message); - let result = await slacktools.processApiMsg(message); - return null; -} - - - - - - -// ========================================== -// ADD SCHEDULE FUNCTIONS -// ========================================== - - - - - -exports.addNewSchedule = addNewSchedule; -async function addNewSchedule(event, data){ - if(log) console.log("ADD NEW SCHEDULE - Event : ", event); - if(log) console.log("ADD NEW SCHEDULE - Data : ", data); - let msg = await addNewScheduleMessage(event, data); - if(log) console.log("ADD NEW SCHEDULE - Message : ", msg); - let postData = { - api_method: "views.open", - b_token: data.b_token - }; - if(log) console.log("ADD NEW SCHEDULE - Post Data : ", postData); - let res = await slacktools.slackApiPost(msg, postData); - if(log) console.log("ADD NEW SCHEDULE - Response : ", res); - return res; -} - - - - - - - -exports.addNewScheduleMessage = addNewScheduleMessage; -async function addNewScheduleMessage(event, data){ - if(log) console.log("ADD NEW SCHEDULE MESSAGE - Data : ", data); - let tz_options = await slacktools.getTimeZoneOptions(); - let _view = { - "type": "modal", - "callback_id": "newjokeschedule-submit", - "private_metadata": JSON.stringify({channel_id: event.channel_id}), - "clear_on_close": true, - "notify_on_close": false, - "title": { "type": "plain_text", "text": "NerdJokes Add Schedule" }, - "submit": { "type": "plain_text", "text": "Submit" }, - "close": { "type": "plain_text", "text": "Cancel" }, - "blocks": [ - { - "type": "section", - "block_id": "newjokeschedule_title", - "text": { - "type": "mrkdwn", - "text": "Please Add, Edit, or Delete your schedule:" - } - },{ - "type": "section", - "block_id": "newjokeschedule_fields", - "fields": [ - { "type": "mrkdwn", "text": "*Team ID:* \n " + event.team_id + " \n " + event.team_domain }, - { "type": "mrkdwn", "text": "*Channel ID:* \n " + event.channel_id } - ] - },{ - "type": "input", - "block_id": "newjokeschedule_days", - "label": { - "type": "plain_text", - "text": "Scheduled Days: (select one or more)" - }, - "element": { - "type": "multi_static_select", - "action_id": "newjokeschedule_days_selected", - "placeholder": { - "type": "plain_text", - "text": "Select schedule days..." - }, - // "initial_options": [], - "options": [ - { - "text": { "type": "plain_text", "text": "Sunday" }, - "value": "0" - }, - { - "text": { "type": "plain_text", "text": "Monday" }, - "value": "1" - }, - { - "text": { "type": "plain_text", "text": "Tuesday" }, - "value": "2" - }, - { - "text": { "type": "plain_text", "text": "Wednesday" }, - "value": "3" - }, - { - "text": { "type": "plain_text", "text": "Thursday" }, - "value": "4" - }, - { - "text": { "type": "plain_text", "text": "Friday" }, - "value": "5" - }, - { - "text": { "type": "plain_text", "text": "Saturday" }, - "value": "6" - } - ] - } - },{ - "type": "input", - "block_id": "newjokeschedule_time", - "label": { - "type": "plain_text", - "text": "Scheduled Time: (select one)" - }, - "element": { - "type": "static_select", - "action_id": "newjokeschedule_time_selected", - "placeholder": { - "type": "plain_text", - "text": "Select schedule time..." - }, - // "initial_option": { }, - "options": [ - { - "text": { "type": "plain_text", "text": "12:00am" }, - "value": "00" - }, - { - "text": { "type": "plain_text", "text": "1:00am" }, - "value": "01" - }, - { - "text": { "type": "plain_text", "text": "2:00am" }, - "value": "02" - }, - { - "text": { "type": "plain_text", "text": "3:00am" }, - "value": "03" - }, - { - "text": { "type": "plain_text", "text": "4:00am" }, - "value": "04" - }, - { - "text": { "type": "plain_text", "text": "5:00am" }, - "value": "05" - }, - { - "text": { "type": "plain_text", "text": "6:00am" }, - "value": "06" - }, - { - "text": { "type": "plain_text", "text": "7:00am" }, - "value": "07" - }, - { - "text": { "type": "plain_text", "text": "8:00am" }, - "value": "08" - }, - { - "text": { "type": "plain_text", "text": "9:00am" }, - "value": "09" - }, - { - "text": { "type": "plain_text", "text": "10:00am" }, - "value": "10" - }, - { - "text": { "type": "plain_text", "text": "11:00am" }, - "value": "11" - }, - { - "text": { "type": "plain_text", "text": "12:00pm" }, - "value": "12" - }, - { - "text": { "type": "plain_text", "text": "1:00pm" }, - "value": "13" - }, - { - "text": { "type": "plain_text", "text": "2:00pm" }, - "value": "14" - }, - { - "text": { "type": "plain_text", "text": "3:00pm" }, - "value": "15" - }, - { - "text": { "type": "plain_text", "text": "4:00pm" }, - "value": "16" - }, - { - "text": { "type": "plain_text", "text": "5:00pm" }, - "value": "17" - }, - { - "text": { "type": "plain_text", "text": "6:00pm" }, - "value": "18" - }, - { - "text": { "type": "plain_text", "text": "7:00pm" }, - "value": "19" - }, - { - "text": { "type": "plain_text", "text": "8:00pm" }, - "value": "20" - }, - { - "text": { "type": "plain_text", "text": "9:00pm" }, - "value": "21" - }, - { - "text": { "type": "plain_text", "text": "10:00pm" }, - "value": "22" - }, - { - "text": { "type": "plain_text", "text": "11:00pm" }, - "value": "23" - } - ] - } - },{ - "type": "input", - "block_id": "newjokeschedule_timezone", - "label": { - "type": "plain_text", - "text": "Scheduled Time Zone: (select one)" - }, - "element": { - "type": "static_select", - "action_id": "newjokeschedule_timezone_selected", - "placeholder": { - "type": "plain_text", - "text": "Select schedule time zone..." - }, - // "initial_option": { }, - "options": tz_options - } - } - ] - }; - // ==#####== GET INITIAL SCHEDULE ==#####== - let schedules = await awstools.readFromDynamo({ - tablename: awstools.jokesScheduleDB, - key_cond_expr: "team_id = :teamid AND channel_id = :channelid", - attrib_vals: { ":teamid": event.team_id , ":channelid": event.channel_id } - }); - if(log) console.log("ADD NEW SCHEDULE MESSAGE - Schedules : ", schedules); - if(schedules.Items.length > 0) { - var schedule = schedules.Items[0]; - if(log) console.log("ADD NEW SCHEDULE MESSAGE - Schedule : ", schedule); - // ==#####== SET DAYS ==#####== - _view.blocks[2].element.initial_options = []; - schedule.days.values.forEach(async function(day) { - _view.blocks[2].element.initial_options.push(_view.blocks[2].element.options[day]); - }); - // ==#####== SET TIME ==#####== - _view.blocks[3].element.initial_option = _view.blocks[3].element.options[parseInt(schedule.hour, 10)]; - // ==#####== SET TIMEZONE ==#####== - _view.blocks[4].element.initial_option = _view.blocks[4].element.options.find(tz => tz.value === schedule.timezone); - } - if(log) console.log("ADD NEW SCHEDULE MESSAGE - View : ", JSON.stringify(_view)); - let message = { - trigger_id: event.trigger_id, - view: JSON.stringify(_view) - }; - if(log) console.log("ADD NEW SCHEDULE MESSAGE - Message : ", message); - return(message); -} - - - - - -exports.addNewScheduleSubmit = addNewScheduleSubmit; -async function addNewScheduleSubmit(event){ - if(log) console.log("ADD NEW SCHEDULE SUBMIT - New Schedule Submitted : ", event); - - var _channel_id = event.view.blocks[1].fields[1].text; - _channel_id = _channel_id.substr( _channel_id.lastIndexOf("+") + 1 , _channel_id.length ); - if(log) console.log("ADD NEW SCHEDULE SUBMIT - New Joke Schedule Submitted - Channel ID : ", _channel_id); - - var days = event.view.state.values.newjokeschedule_days.newjokeschedule_days_selected.selected_options ; // array - var days_set = []; - days.forEach(async function(day) { - //days_set.push(day.value.toString()); - days_set.push(parseInt(day.value, 10)); - //days_text = (days_text.length == 0) ? days_text += day.value : days_text += "," + day.value - }); - if(log) console.log("ADD NEW SCHEDULE SUBMIT - New Joke Schedule Submitted - Days : ", days_set.toString()); - var hour = event.view.state.values.newjokeschedule_time.newjokeschedule_time_selected.selected_option.value ; // str to num - var tz = event.view.state.values.newjokeschedule_timezone.newjokeschedule_timezone_selected.selected_option.value ; // str to num - var _gmt_hour = parseInt(hour, 10) - ( parseInt(tz, 10) ) ; - if (_gmt_hour > 24) _gmt_hour = _gmt_hour - 24 ; - - let res = await awstools.writeToDynamo({ - tablename: awstools.jokesScheduleDB, - item: { - team_id : event.team.id , - channel_id : _channel_id , - days : await awstools.createDynamoSet(days_set) , - hour : parseInt(hour, 10) , - timezone : tz , - gmt_hour : _gmt_hour , - put_date : new Date().toISOString() - } - }); -} - - - - - -exports.addNewScheduleThankYou = addNewScheduleThankYou; -async function addNewScheduleThankYou(event, data){ - if(log) console.log("ADD NEW SCHEDULE THANK YOU - Event : ", event); - if(log) console.log("ADD NEW SCHEDULE THANK YOU - Data : ", data); - var prvmeta = JSON.parse(event.view.private_metadata); - let message = { - token: data.b_token, - replace_original: true, - channel: prvmeta.channel_id, - response_type: 'ephemeral' , - text: "The joke schedule for this channel has been successfully created." , - b_token: data.b_token , - api_method: "chat.postMessage" , - response_url: event.response_url - }; - if(log) console.log("ADD NEW SCHEDULE THANK YOU - Result : ", message); - let result = await slacktools.processApiMsg(message); - return null; -} - - - - -// ========================================== -// DELETE SCHEDULE FUNCTIONS -// ========================================== - - - -exports.deleteSchedule = deleteSchedule; -async function deleteSchedule(event, data){ - if(log) console.log("DELETE SCHEDULE - Event : ", event); - if(log) console.log("DELETE SCHEDULE - Data : ", data); - let msg; - let postData; - let schedule = await awstools.readFromDynamo({ - tablename: awstools.jokesScheduleDB, - key_cond_expr: "team_id = :teamid AND channel_id = :channelid" , - attrib_vals: { ":teamid": data.team_id , ":channelid": event.channel_id } - }); - if(schedule.Items.length > 0) { - data.schedule = schedule; - msg = await deleteScheduleMessage(event, data); - if(log) console.log("DELETE SCHEDULE - Message : ", msg); - postData = { - api_method: "views.open", - b_token: data.b_token - }; - } else { - msg = await noScheduleMessage(event, data); - if(log) console.log("DELETE SCHEDULE - Message : ", msg); - postData = { - api_method: "chat.postMessage" , - b_token: data.b_token, - response_url: event.response_url - }; - } - if(log) console.log("DELETE SCHEDULE - Post Data : ", postData); - let res = await slacktools.slackApiPost(msg, postData); - if(log) console.log("DELETE SCHEDULE - Response : ", res); - return null; -} - - - - -exports.deleteScheduleMessage = deleteScheduleMessage; -async function deleteScheduleMessage(event, data){ - if(log) console.log("DELETE SCHEDULE MESSAGE - Data : ", data); - let _view = JSON.stringify({ - "type": "modal", - "callback_id": "deleteschedule-submit", - "private_metadata": JSON.stringify({channel_id: event.channel_id}), - "clear_on_close": true, - "notify_on_close": false, - "title": { - "type": "plain_text", - "text": "NerdJokes DeleteSchedule" - }, - "submit": { - "type": "plain_text", - "text": "Submit" - }, - "close": { - "type": "plain_text", - "text": "Cancel" - }, - "blocks": [ - { - "type": "section", - "block_id": "deleteschedule_title", - "text": { - "type": "plain_text", - "text": "Are you sure you want to delete the schedule for this workspace and channel?" - } - },{ - "type": "section", - "block_id": "deleteschedule_fields", - "fields": [ - { "type": "mrkdwn", "text": "*Team ID:* \n " + event.team_id + " \n " + event.team_domain }, - { "type": "mrkdwn", "text": "*Channel ID:* \n " + event.channel_id }, - { "type": "mrkdwn", "text": "*Days:* \n " + data.schedule.Items[0].days.values.toString() }, - { "type": "mrkdwn", "text": "*Hour:* \n " + data.schedule.Items[0].hour }, - { "type": "mrkdwn", "text": "*Timezone:* \n " + data.schedule.Items[0].timezone } - ] - },{ - "type": "input", - "block_id": "deleteschedule_confirm", - "label": { - "type": "plain_text", - "text": "Confirm Delete" - }, - "element": { - "type": "checkboxes", - "action_id": "deleteschedule_selected", - "options": [ - { - "text": { - "type": "plain_text", - "text": "Yes, please delete this schedule" - }, - "value": "schedule_delete" - } - ] - } - } - ] - }); - if(log) console.log("DELETE SCHEDULE - View : ", _view); - let message = { - trigger_id: event.trigger_id, - view: _view - }; - if(log) console.log("DELETE SCHEDULE - Message : ", message); - return(message); -} - - - - - -exports.noScheduleMessage = noScheduleMessage; -async function noScheduleMessage(event, data){ - if(log) console.log("NO SCHEDULE MESSAGE - Event : ", event); - if(log) console.log("NO SCHEDULE MESSAGE - Data : ", data); - let message = { - token: data.b_token, - replace_original: true, - channel: event.channel_id, - response_type: 'ephemeral' , - text: "There is no schedule for this channel to be deleted.", - b_token: data.b_token , - api_method: "chat.postMessage" , - response_url: event.response_url - }; - if(log) console.log("NO SCHEDULE MESSAGE - Result : ", message); - return (message); -} - - - - - -exports.deleteScheduleSubmit = deleteScheduleSubmit; -async function deleteScheduleSubmit(event){ - if(log) console.log("DELETE SCHEDULE SUBMIT - Submitted : ", event); - var _channel_id = event.view.blocks[1].fields[1].text; - _channel_id = _channel_id.substr( _channel_id.lastIndexOf("+") + 1 , _channel_id.length ); - if(log) console.log("DELETE SCHEDULE SUBMIT - Submitted - Channel ID : ", _channel_id); - var del = event.view.state.values.deleteschedule_confirm.deleteschedule_selected.selected_options ; // str to num - if(del[0].value && del[0].value == "schedule_delete"){ - let res = await awstools.deleteFromDynamo({ - tablename: awstools.jokesScheduleDB, - key: { team_id : event.team.id , channel_id: _channel_id }, - }); - } -} - - - - - -exports.deleteScheduleThankYou = deleteScheduleThankYou; -async function deleteScheduleThankYou(event, data){ - if(log) console.log("DELETE SCHEDULE THANK YOU - Event : ", event); - if(log) console.log("DELETE SCHEDULE THANK YOU - Data : ", data); - var prvmeta = JSON.parse(event.view.private_metadata); - let message = { - token: data.b_token, - replace_original: true, - channel: prvmeta.channel_id, - response_type: 'ephemeral' , - text: "The joke schedule for this channel has been successfully deleted.", - b_token: data.b_token , - api_method: "chat.postMessage" , - response_url: event.response_url - }; - if(log) console.log("DELETE SCHEDULE THANK YOU - Result : ", message); - let result = await slacktools.processApiMsg(message); - return null; -} - - - - -// ========================================== -// RUN SCHEDULE FUNCTIONS -// ========================================== - - - - - - -exports.runSchedule = runSchedule; -async function runSchedule(data){ - if(log) console.log("RUN SCHEDULE "); - if(log) console.log("RUN SCHEDULE - Data : ", data); - let schedules = await awstools.readFromDynamo({ - tablename: awstools.jokesScheduleDB, - indexname: "gmt_hour-index", - key_cond_expr: "gmt_hour = :hour", - filter_expr: "contains(days, :day)", - attrib_vals: { ":hour": data.gmt_hour , ":day": data.day } - }); - if(log) console.log("RUN SCHEDULE - Schedules : ", schedules); - if(schedules.Items.length == 0 ) { - return null ; - } - if(log) console.log("RUN SCHEDULE - Schedule Count : ", schedules.Items.length); - for (var schedule of schedules.Items){ - //await channels.Items.forEach(async function(channel) { - if(log) console.log("RUN SCHEDULE - Channel : ", schedule); - // ==#####== GET CUSTOM TOKEN PER WORKSPACE ==#####== - let b_token = await getAccessToken({ team_id: schedule.team_id }); - // var event = { channel_id: schedule.channel_id }; - // var _data = { b_token: b_token }; - // ==#####== SEND RANDOM JOKE ==#####== - // let sent = await sendJoke(event, _data); - var lambdaPayload = { - "httpMethod": "POST", - "headers": { - "Content-Type": "application/json", - }, - "body": { - "token": b_token, - "team_id": schedule.team_id, - "channel_id": schedule.channel_id, - "command": "/nerdjokes", - "text": "getjoke", - } - }; - if(log) console.log("RUN SCHEDULE - Lambda - Payload : ", lambdaPayload); - let lambdaParams = { - FunctionName: 'nerdjokes', - InvocationType: 'Event', - Payload: JSON.stringify(lambdaPayload) - }; - if(log) console.log("RUN SCHEDULE - Lambda - Params : ", lambdaParams); - var push = awstools.callLambda(lambdaParams); - } -} \ No newline at end of file From 818e0a54185e8d645b76db8217bce9a9a9f0789b Mon Sep 17 00:00:00 2001 From: Brian Whaley Date: Thu, 28 May 2020 16:19:34 -0400 Subject: [PATCH 3/7] v3.0.0 v3.0.0 --- tools-aws.js | 460 --------------------------------------------------- 1 file changed, 460 deletions(-) delete mode 100644 tools-aws.js diff --git a/tools-aws.js b/tools-aws.js deleted file mode 100644 index 85fba78..0000000 --- a/tools-aws.js +++ /dev/null @@ -1,460 +0,0 @@ -console.log('Loading tools-aws'); - -const AWS = require("aws-sdk"); -AWS.config.update({region: 'us-east-2'}); -const S3 = new AWS.S3(); -const DDB = new AWS.DynamoDB({apiVersion: '2012-08-10'}); -const DDBCLIENT = new AWS.DynamoDB.DocumentClient(); -const SES = new AWS.SES({region: 'us-east-1'}); -const LAMBDA = new AWS.Lambda(); - -const stkrS3Bucket = exports.stkrS3Bucket = "stkr-bucket"; -const stkrTokenDB = exports.stkrTokenDB = "stkr-tokens"; -const stkrImageDB = exports.stkrImageDB = "stkr-images"; -const stkrLogDB = exports.stkrLogDB = "stkr-logs"; -const jokesTokenDB = exports.jokesTokenDB = "nerdjokes-tokens"; -const jokesDB = exports.jokesDB = "nerdjokes"; -const jokesScheduleDB = exports.jokesScheduleDB = "nerdjokes-schedule"; -const jokesLogDB = exports.jokesLogDB = "nerdjokes-logs"; -const log = true; - - - - - -// ======================================== -// S3 FUNCTIONS -// ======================================== - - - - -exports.isS3Object = isS3Object; -async function isS3Object(data){ - if(log) console.log("ISS3OBJECT - Data : ", data); - try { - const params = { - Bucket: data.bucket, - Key: data.team_id + "/" + data.filename - }; - const result = await S3.headObject(params).promise(); - return true; - } catch(error) { - console.log("IS S3 OBJECT - Error", JSON.stringify(error)); - console.log("IS S3 OBJECT - ERROR Code : ", JSON.stringify(error.code)); - console.log("IS S3 OBJECT - ERROR Message : ", JSON.stringify(error.message)); - console.log("IS S3 OBJECT - ERROR Stack : ", JSON.stringify(error.stack)); - //if (error.code === 'NotFound') { - return false; - //} - } -} - - - - - -exports.uploadImgToS3 = uploadImgToS3; -async function uploadImgToS3(buffer, data){ - // ==#####== REPLACE SPACES WITH DASHES ==#####== - var filename = data.filename.replace(/\s+/g, '-'); - const params = { - Bucket: data.bucket, - Key: data.team_id + "/" + filename, //data.filename, - Body: buffer, - ContentType: "image", - ACL: 'public-read-write' - }; - return new Promise((resolve, reject) => { - S3.putObject(params, (error) => { - if (error) { - console.log("UPLOAD TO S3 - Error", JSON.stringify(error)); - console.log("UPLOAD TO S3 - ERROR Code : ", JSON.stringify(error.code)); - console.log("UPLOAD TO S3 - ERROR Message : ", JSON.stringify(error.message)); - console.log("UPLOAD TO S3 - ERROR Stack : ", JSON.stringify(error.stack)); - reject(error); - } else { - if(log) console.log("UPLOADIMGTOS3 - " + filename + " uploaded to " + params.Bucket + " succesfully."); - resolve(filename); - } - }); - }); - -} - - - - - - -exports.deleteImgFromS3 = deleteImgFromS3; -async function deleteImgFromS3(data){ - const params = { - Bucket: data.bucket, - Key: data.team_id + "/" + data.filename - }; - return new Promise((resolve, reject) => { - S3.deleteObject(params, (error) => { - if (error) { - console.log("DELETE FROM S3 - Error", JSON.stringify(error)); - console.log("DELETE FROM S3 - ERROR Code : ", JSON.stringify(error.code)); - console.log("DELETE FROM S3 - ERROR Message : ", JSON.stringify(error.message)); - console.log("DELETE FROM S3 - ERROR Stack : ", JSON.stringify(error.stack)); - reject(error); - } else { - if(log) console.log("DELETE FROM S3 - Successful - Deleted - " + data.filename ); - resolve(data.filename); - } - }); - }); - -} - - - - -exports.emptyS3Directory = emptyS3Directory; -async function emptyS3Directory(data) { - const listParams = { - Bucket: data.bucket, - Prefix: data.team_id + "/" - }; - const listedObjects = await S3.listObjectsV2(listParams).promise(); - if (listedObjects.Contents.length === 0) return null; - const deleteParams = { - Bucket: data.bucket, - Delete: { Objects: [] } - }; - listedObjects.Contents.forEach(({ Key }) => { - deleteParams.Delete.Objects.push({ Key }); - }); - await S3.deleteObjects(deleteParams).promise(); - if (listedObjects.IsTruncated) await emptyS3Directory(data); -} - - - - -exports.getS3ItemCount = getS3ItemCount; -async function getS3ItemCount(data){ - if(log) console.log("getS3ItemCount - Event : ", data); - var params = { - Bucket: data.bucket, - Prefix: data.team_id + "/" - }; - let retdata; - let items = 0; - retdata = await S3.listObjectsV2(params).promise(); - items = retdata.Contents.length ; - if(log) console.log("getS3ItemCount - Image Count : ", items); - return(items); -} - - - - - -exports.getS3ItemList = getS3ItemList; -async function getS3ItemList(data){ - if(log) console.log("GET S3 ITEM LIST - Event : ", data); - var params = { - Bucket: data.bucket, - Prefix: data.team_id + "/" - }; - let retdata; - let images = []; - return new Promise((resolve, reject) => { - S3.listObjectsV2(params, (error, retdata) => { - if (error) { - console.log("GET S3 ITEM LIST - Error", JSON.stringify(error)); - console.log("GET S3 ITEM LIST - ERROR Code : ", JSON.stringify(error.code)); - console.log("GET S3 ITEM LIST - ERROR Message : ", JSON.stringify(error.message)); - console.log("GET S3 ITEM LIST - ERROR Stack : ", JSON.stringify(error.stack)); - reject(error); - } else { - if(log) console.log("GET S3 ITEM LIST - Success"); - for (let index = 0; index < retdata.Contents.length; index++) { - var key = retdata.Contents[index].Key; - // images += key.replace(params.Prefix, '') + " \n"; - // var item = { text: key.replace(params.Prefix, "") , value: key.replace(params.Prefix, "") }; - var item = { "text": { "type": "plain_text", "text": key.replace(params.Prefix, "") } , value: key.replace(params.Prefix, "") } ; - images.push(item); - } - if(log) console.log("GET S3 ITEM LIST - Data : ", JSON.stringify(images)); - resolve(images); - } - }); - }); -} - - - - - -// ======================================== -// DYNAMODB FUNCTIONS -// ======================================== - - - - -exports.readFromDynamo = readFromDynamo; -async function readFromDynamo(data){ - if(log) console.log("READ FROM DYNAMO - Data : ", data); - var params = { TableName: data.tablename }; - // if(data.key) params.Key = { [data.key] : data.value }; - if(data.key) params.Key = data.key; - if(data.indexname) params.IndexName = data.indexname ; - if(data.key_cond_expr) params.KeyConditionExpression = data.key_cond_expr ; - if(data.cond_expr) params.ConditionExpression = data.cond_expr ; - if(data.filter_expr) params.FilterExpression = data.filter_expr ; - if(data.attrib_vals) params.ExpressionAttributeValues = data.attrib_vals ; - if(data.proj_expr) params.ProjectionExpression = data.proj_expr ; - if(data.select) params.Select = data.select ; - if(data.limit) params.Limit = data.limit ; - if(log) console.log("READ FROM DYNAMO - Params : ", params); - return new Promise((resolve, reject) => { - DDBCLIENT.query(params, (error, itemData) => { - if (error) { - console.log("READ FROM DYNAMO - Error", JSON.stringify(error)); - console.log("READ FROM DYNAMO - ERROR Code : ", JSON.stringify(error.code)); - console.log("READ FROM DYNAMO - ERROR Message : ", JSON.stringify(error.message)); - console.log("READ FROM DYNAMO - ERROR Stack : ", JSON.stringify(error.stack)); - reject(error); - } else { - if(log) console.log("READ FROM DYNAMO - Success"); - resolve(itemData); - } - }); - }); -} - - - -exports.scanFromDynamo = scanFromDynamo; -async function scanFromDynamo(data){ - if(log) console.log("SCAN FROM DYNAMO - Data : ", data); - var params = { TableName: data.tablename }; - if(data.key) params.Key = data.key; - if(data.indexname) params.IndexName = data.indexname ; - if(data.key_cond_expr) params.KeyConditionExpression = data.key_cond_expr ; - if(data.cond_expr) params.ConditionExpression = data.cond_expr ; - if(data.filter_expr) params.FilterExpression = data.filter_expr ; - if(data.attrib_names) params.ExpressionAttributeNames = data.attrib_names ; - if(data.attrib_vals) params.ExpressionAttributeValues = data.attrib_vals ; - if(data.proj_expr) params.ProjectionExpression = data.proj_expr ; - if(data.select) params.Select = data.select ; - if(data.start_key) params.ExclusiveStartKey = data.start_key; - if(data.limit) params.Limit = data.limit ; - if(log) console.log("SCAN FROM DYNAMO - Params : ", params); - return new Promise((resolve, reject) => { - DDBCLIENT.scan(params, (error, result) => { - if (error) { - console.log("SCAN FROM DYNAMO - Error", JSON.stringify(error)); - console.log("SCAN FROM DYNAMO - ERROR Code : ", JSON.stringify(error.code)); - console.log("SCAN FROM DYNAMO - ERROR Message : ", JSON.stringify(error.message)); - console.log("SCAN FROM DYNAMO - ERROR Stack : ", JSON.stringify(error.stack)); - reject(error); - } else { - if(log) console.log("SCAN FROM DYNAMO - Success"); - if(log) console.log("SCAN FROM DYNAMO - Results : ", result); - resolve(result); - } - }); - }); -} - - - - -exports.writeToDynamo = writeToDynamo; -async function writeToDynamo(data){ - if(log) console.log("WRITE TO DYNAMO - Data", JSON.stringify(data)); - var params = { - TableName: data.tablename, - Item: data.item - }; - if(log) console.log("WRITE TO DYNAMO - Params", JSON.stringify(params)); - return new Promise((resolve, reject) => { - DDBCLIENT.put(params, (error) => { - if (error) { - console.log("WRITE TO DYNAMO - Error", JSON.stringify(error)); - console.log("WRITE TO DYNAMO - ERROR Code : ", JSON.stringify(error.code)); - console.log("WRITE TO DYNAMO - ERROR Message : ", JSON.stringify(error.message)); - console.log("WRITE TO DYNAMO - ERROR Stack : ", JSON.stringify(error.stack)); - reject(error); - } else { - if(log) console.log("WRITE TO DYNAMO - Success"); - resolve("Data written to dynamo succesfully."); - } - }); - }); -} - - - - -exports.createDynamoSet = createDynamoSet; -async function createDynamoSet(data){ - if(log) console.log("CREATE DYNAMO SET"); - if(log) console.log("CREATE DYNAMO SET - Data", data); - return DDBCLIENT.createSet(data) ; -} - - - -exports.updateToDynamo = updateToDynamo; -async function updateToDynamo(data){ - if(log) console.log("UPDATE TO DYNAMO - Data", data); - var params = { TableName: data.tablename }; - // if(data.key) params.Key = { [data.key] : data.value }; - if(data.key) params.Key = data.key; - if(data.update_expr) params.UpdateExpression = data.update_expr ; - if(data.attrib_vals) params.ExpressionAttributeValues = data.attrib_vals ; - if(data.return_vals) params.ReturnValues = data.return_vals ; - if(log) console.log("UPDATE TO DYNAMO - Params", params); - return new Promise((resolve, reject) => { - DDBCLIENT.update(params, (error) => { - if (error) { - console.log("UPDATE TO DYNAMO - Error", JSON.stringify(error)); - console.log("UPDATE TO DYNAMO - ERROR Code : ", JSON.stringify(error.code)); - console.log("UPDATE TO DYNAMO - ERROR Message : ", JSON.stringify(error.message)); - console.log("UPDATE TO DYNAMO - ERROR Stack : ", JSON.stringify(error.stack)); - reject(error); - } else { - if(log) console.log("UPDATE TO DYNAMO - Success"); - resolve("Data updated to dynamo succesfully."); - } - }); - }); -} - - - - - - -exports.deleteFromDynamo = deleteFromDynamo; -async function deleteFromDynamo(data){ - if(log) console.log("DELETE FROM DYNAMO - Data : ", data); - var params = { TableName: data.tablename }; - if(data.key) params.Key = data.key; - if(data.indexname) params.IndexName = data.indexname ; - if(data.key_cond_expr) params.KeyConditionExpression = data.key_cond_expr ; - if(data.cond_expr) params.ConditionExpression = data.cond_expr ; - if(data.filter_expr) params.FilterExpression = data.filter_expr ; - if(data.attrib_vals) params.ExpressionAttributeValues = data.attrib_vals ; - if(data.proj_expr) params.ProjectionExpression = data.proj_expr ; - if(log) console.log("DELETE FROM DYNAMO - Params : ", params); - return new Promise((resolve, reject) => { - DDBCLIENT.delete(params, (error, itemData) => { - if (error) { - console.log("DELETE FROM DYNAMO - Error", JSON.stringify(error)); - console.log("DELETE FROM DYNAMO - ERROR Code : ", JSON.stringify(error.code)); - console.log("DELETE FROM DYNAMO - ERROR Message : ", JSON.stringify(error.message)); - console.log("DELETE FROM DYNAMO - ERROR Stack : ", JSON.stringify(error.stack)); - reject(error); - } else { - if(log) console.log("DELETE FROM DYNAMO - Success"); - if(log) console.log("DELETE FROM DYNAMO - Delete Data", itemData); - resolve(itemData); - } - }); - }); -} - - - - - - -exports.batchDeleteFromDynamo = batchDeleteFromDynamo; -async function batchDeleteFromDynamo(data){ - if(log) console.log("BATCH DELETE FROM DYNAMO - Data : ", JSON.stringify(data)); - var params = data.params ; - if(log) console.log("BATCH DELETE FROM DYNAMO - Params : ", JSON.stringify(params)); - return new Promise((resolve, reject) => { - DDBCLIENT.batchWrite(params, (error, itemData) => { - if (error) { - console.log("BATCH DELETE FROM DYNAMO - Error", JSON.stringify(error)); - console.log("BATCH DELETE FROM DYNAMO - ERROR Code : ", JSON.stringify(error.code)); - console.log("BATCH DELETE FROM DYNAMO - ERROR Message : ", JSON.stringify(error.message)); - console.log("BATCH DELETE FROM DYNAMO - ERROR Stack : ", JSON.stringify(error.stack)); - reject(error); - } else { - if(log) console.log("BATCH DELETE FROM DYNAMO - Success"); - if(log) console.log("BATCH DELETE FROM DYNAMO - Delete Data", itemData); - resolve(itemData); - } - }); - }); -} - - - - - -// ======================================== -// SES FUNCTIONS -// ======================================== - - - - -exports.sendEmail = sendEmail; -async function sendEmail(data){ - var params = { - Destination: { - ToAddresses: [ data.to ] - }, - Message: { - Body: { Text: { Data: (data.body) ? data.body : data.subject } }, - Subject: { Data: data.subject } - }, - Source: data.from - }; - return new Promise((resolve, reject) => { - SES.sendEmail(params, function (error, emaildata) { - if (error) { - console.log("SEND EMAIL - ERROR", JSON.stringify(error)); - console.log("SEND EMAIL - ERROR Code : ", JSON.stringify(error.code)); - console.log("SEND EMAIL - ERROR Message : ", JSON.stringify(error.message)); - console.log("SEND EMAIL - ERROR Stack : ", JSON.stringify(error.stack)); - reject(error); - } else { - if(log) console.log("SEND EMAIL - Success"); - if(log) console.log("SEND EMAIL - Success Data", emaildata); - resolve(emaildata); - } - }); - }); -} - - - - -// ======================================== -// S3 FUNCTIONS -// ======================================== - - - - -exports.callLambda = callLambda; -async function callLambda(params){ - return new Promise((resolve, reject) => { - LAMBDA.invoke(params, function(error, data) { - if (error) { - console.log("CALL LAMBDA - ERROR", JSON.stringify(error)); - console.log("CALL LAMBDA - ERROR Code : ", JSON.stringify(error.code)); - console.log("CALL LAMBDA - ERROR Message : ", JSON.stringify(error.message)); - console.log("CALL LAMBDA - ERROR Stack : ", JSON.stringify(error.stack)); - reject(error); - } else { - if(log) console.log("CALL LAMBDA - Success"); - if(log) console.log("CALL LAMBDA - Success Data", data); - resolve(data); - } - }); - }); -} \ No newline at end of file From 440af410c146abbbf595ea7bcc9d86430f74df1b Mon Sep 17 00:00:00 2001 From: Brian Whaley Date: Thu, 28 May 2020 16:19:48 -0400 Subject: [PATCH 4/7] v3.0.0 v3.0.0 --- tools-slack.js | 481 ------------------------------------------------- 1 file changed, 481 deletions(-) delete mode 100644 tools-slack.js diff --git a/tools-slack.js b/tools-slack.js deleted file mode 100644 index cd00c18..0000000 --- a/tools-slack.js +++ /dev/null @@ -1,481 +0,0 @@ -console.log('Loading tools-slack'); - -var awstools = require("./tools-aws"); - -const HTTP = require('http'); -const HTTPS = require('https'); -const URL = require('url'); -const QS = require('querystring'); - -const nerdjokes_url = "https://pixelated.tech/nerdjokes.html" ; -const log = true ; - - - - - -exports.querystringToJSON = querystringToJSON; -function querystringToJSON(qs) { - var pairs = qs.split('&'); - var result = {}; - pairs.forEach(function(pair) { - pair = pair.split('='); - result[pair[0]] = decodeURIComponent(pair[1] || ''); - }); - return JSON.parse(JSON.stringify(result)); -} - - - - -exports.getRandomInt = getRandomInt; -function getRandomInt(min, max) { - // min and max are inclusive - min = Math.ceil(min); - max = Math.floor(max); - return Math.floor(Math.random() * (max - min + 1)) + min; -} - - - - -exports.sortByProperty = sortByProperty; -function sortByProperty(property){ - return function(a,b){ - if(a[property] > b[property]) - return 1; - else if(a[property] < b[property]) - return -1; - return 0; - }; -} - - - - -exports.urlExists = urlExists; -async function urlExists(url) { - if(log) console.log("URL EXISTS "); - if(log) console.log("URL EXISTS - URL : ", url); - const request = (opts = {}, cb) => { - const requester = opts.protocol === 'https:' ? HTTPS : HTTP; - return requester.request(opts, cb); - }; - var options = { - method: 'HEAD', - port: URL.parse(url).port, - protocol: URL.parse(url).protocol, - host: URL.parse(url).host, - path: URL.parse(url).pathname, - }; - return new Promise(function(resolve, reject) { - var req = request(options, function(res) { - if(log) console.log("URL EXISTS - Results ", res); - if(log) console.log("URL EXISTS - Status Code : ", res.statusCode); - if(log) console.log("URL EXISTS - Headers : ", JSON.stringify(res.headers)); - if(log) console.log("URL EXISTS - Content Type : ", res.headers['content-type']); - /* - if(res.statusCode == 200){ - resolve(true); - } else { - resolve(false); - } - */ - resolve(res); - }); - req.on("error", function (error) { - if(log) console.log("URL EXISTS - ERROR : ", JSON.stringify(error)); - if(log) console.log("URL EXISTS - ERROR Code : ", JSON.stringify(error.code)); - if(log) console.log("URL EXISTS - ERROR Message : ", JSON.stringify(error.message)); - if(log) console.log("URL EXISTS - ERROR Stack : ", JSON.stringify(error.stack)); - reject(false); - }); - req.end(); - }); -} - - - - -exports.getTeamId = getTeamId; -function getTeamId(event, env){ - if(log) console.log("GET TEAM ID "); - if(log) console.log("GET TEAM ID - Event : ", event); - if(log) console.log("GET TEAM ID - ENV : ", env); - var team_id ; - if (event.team_id) { team_id = event.team_id ; } - else if(event.team) { team_id = event.team.id ; } - else { team_id = "." } - var retval = (env != "PROD") ? team_id + "-" + env : team_id ; - if(log) console.log("GET TEAM ID - Ret Val : ", retval); - return retval ; -} - - - - -exports.urlVerify = urlVerify; -function urlVerify(e_challenge, e_token, v_token) { - if(log) console.log("URL VERIFY - Event Challenge "); - if(log) console.log("URL VERIFY - E-C, E-T, V-T : ", e_challenge, e_token, v_token); - var result; - // ==#####== CHALLENGE ==#####== - if (e_token === v_token) { - result = { challenge: e_challenge }; - } else { - result = "URL VERIFY - Verification Failed"; - } - if(log) console.log("VERIFICATION - Results : ", result); - return { - statusCode: 200, - isBase64Encoded: false, - headers: {"content-type": "application/x-www-form-urlencoded"}, - body: JSON.stringify(result) - }; -} - - - - -exports.oAuthVerify = oAuthVerify; -async function oAuthVerify(event, data) { - if(log) console.log("OAUTH VERIFY - OAuth - App Install "); - if(log) console.log("OAUTH VERIFY - OAuth - Event : ", event); - if(log) console.log("OAUTH VERIFY - OAuth - Data : ", data); - const oaMsg = { - client_id: data.client_id, - client_secret: data.client_secret, - code: event.code , - }; - const oaData = { - // b_token: data.b_token, - api_method: "oauth.v2.access" - }; - if(log) console.log("OAUTH VERIFY - OAuth oaMsg : ", oaMsg ); - if(log) console.log("OAUTH VERIFY - OAuth oaData : ", oaData); - var oauthaccess = await slackApiGet(oaMsg, oaData); - if(log) console.log("OAUTH VERIFY - OAuth - Verify : ", oauthaccess); - var team_id = getTeamId(oauthaccess, data.env) ; - const storeAuth = await awstools.writeToDynamo({ - tablename: awstools.jokesTokenDB, - item: { - team_id : team_id , - access_token : oauthaccess.access_token , - response : JSON.stringify(oauthaccess) , - put_date : new Date().toISOString() - } - }); - if(log) console.log("OAUTH VERIFY - OAuth - Key Store : ", storeAuth); - const email_res = await awstools.sendEmail({ - from: "nerdjokes@pixelated.tech", - to: "nerdjokes@pixelated.tech", - subject: "New NerdJokes App Install!" - }); - if(log) console.log("OAUTH VERIFY - OAuth - Email Sent : ", email_res); - // ==#####== LOG EVENT ==@@@@@== - const logged = await logEvent(event, { team_id: team_id }); - return { - statusCode: 302, - headers: { - "Location": nerdjokes_url - }, - body: null - }; -} - - - - - -exports.logEvent = logEvent; -async function logEvent(event, data){ - if(log) console.log("LOG EVENT - Event : ", event); - if(log) console.log("LOG EVENT - Data : ", data); - // ==#####== EVENT TYPE ==#####== - var eventType = null; - if(event.command && event.text) { eventType = event.command + " " + event.text ; } - else if (event.event) { eventType = event.event.type ; } - else if (event.view) { eventType = event.view.callback_id ; } - else if (event.actions){ eventType = event.actions[0].action_id ; } - else if (event.code) { eventType = "app_installed" ; } - if(log) console.log("LOG EVENT - eventType : ", eventType); - // ==#####== USER ID ==#####== - var userId = null; - if (event.user_id) { userId = event.user_id; } - else if (event.user) { userId = event.user.id ; } - else if(event.authed_user) { userId = event.authed_user.id ; } - else { userId = "." ; } - if(log) console.log("LOG EVENT - userId : ", userId); - // ==#####== CHANNEL ID ==#####== - var channelId = null ; - if (event.channel_id){ channelId = event.channel_id; } - else if (event.channel){ channelId = event.channel.id ; } - else if (event.view && event.view.private_metadata) { channelId = JSON.parse(event.view.private_metadata).channel_id ; } - else { channelId = "." ; } - // ==#####== LOG TO DYNAMO ==#####== - var logged = await awstools.writeToDynamo({ - tablename: awstools.jokesLogDB, - item: { - team_id : data.team_id , - log_id: Date.now().valueOf().toString() , - channel_id : channelId , - event_type : eventType , //image upload, image share - user_id : userId - } - }); - if(log) console.log("LOG EVENT - Logged : ", logged); - - return null; -} - - - - -exports.getFileInfo = getFileInfo; -async function getFileInfo(file_id, b_token){ - // ==#####== GET FILE INFO ==#####== - if(log) console.log("FILE INFO - FILE ID : ", file_id); - var message = { - token: b_token, - file: file_id - }; - if(log) console.log("FILE INFO - Request Message : ", message); - var qstring = QS.stringify(message); - if(log) console.log("FILE INFO - Request QString : ", qstring); - var options = { - method: "GET", - port: 443, - protocol: "https:", - hostname: "slack.com", - path: "/api/files.info?" + qstring, - }; - if(log) console.log("FILE INFO - Request File Options : ", options); - let retJSON = ''; - return new Promise(function(resolve, reject) { - const request = HTTPS.request(options, function(res) { - if(log) console.log('FILE INFO - Request Status Code:', res.statusCode); - if(log) console.log('FILE INFO - Request Headers:', res.headers); - res.setEncoding( 'utf8' ); - res.on("data", function(data) { - retJSON += data; - }); - res.on("end", function () { - if(log) console.log("FILE INFO - Response Results", retJSON.toString()); - resolve(JSON.parse(retJSON)); - }); - }); - request.on("error", function (error) { - console.log("FILE INFO - ERROR : ", JSON.stringify(error)); - console.log("FILE INFO - ERROR Code : ", JSON.stringify(error.code)); - console.log("FILE INFO - ERROR Message : ", JSON.stringify(error.message)); - console.log("FILE INFO - ERROR Stack : ", JSON.stringify(error.stack)); - reject(error); - }); - request.end(); - if(log) console.log("FILE INFO - Request Results : ", request); - }); -} - - - - - -exports.fetchImage = fetchImage; -async function fetchImage(data){ - var imgBuffer; - var options = { - headers: { - "Authorization": "Bearer " + data.b_token - } - }; - return new Promise(function(resolve, reject) { - const request = HTTPS.get(data.url, options, function(res) { - if(log) console.log('FETCH IMAGE - Request Status Code:', res.statusCode); - if(log) console.log('FETCH IMAGE - Request Headers:', res.headers); - // res.setEncoding('base64'); - var retIMG = Buffer.alloc(0); - res.on("data", function(chunk) { - retIMG = Buffer.concat([retIMG, chunk]); - }); - res.on("end", function () { - imgBuffer = retIMG; - // if(log) console.log("FETCH IMAGE - Response Results : ", imgBuffer.toString()); - if(log) console.log("FETCH IMAGE - Response Results : SUCCESS"); - resolve(imgBuffer); - }); - }); - request.on("error", function (error) { - console.log("FILE INFO - ERROR : ", JSON.stringify(error)); - console.log("FILE INFO - ERROR Code : ", JSON.stringify(error.code)); - console.log("FILE INFO - ERROR Message : ", JSON.stringify(error.message)); - console.log("FILE INFO - ERROR Stack : ", JSON.stringify(error.stack)); - reject(error); - }); - request.end(); - if(log) console.log("FETCH IMAGE - Request Results : ", request); - }); -} - - - - - -exports.slackApiGet = slackApiGet; -async function slackApiGet(message, data){ - // ==#####== SLACK API GET ==#####== - if(log) console.log("SLACK API GET - Message : ", message); - if(log) console.log("SLACK API GET - Data : ", data); - var qstring = QS.stringify(message); - if(log) console.log("SLACK API GET - Request QString : ", qstring); - var options = { - method: "GET", - port: 443, - protocol: "https:", - hostname: "slack.com", - path: "/api/" + data.api_method + "?" + qstring - }; - if(log) console.log("SLACK API GET - Request File Options : ", options); - let retJSON = ''; - return new Promise(function(resolve, reject) { - const request = HTTPS.request(options, function(res) { - if(log) console.log('SLACK API GET - Request Status Code:', res.statusCode); - if(log) console.log('SLACK API GET - Request Headers:', res.headers); - res.setEncoding( 'utf8' ); - res.on("data", function(data) { - retJSON += data; - }); - res.on("end", function () { - if(log) console.log("SLACK API GET - Response Results", retJSON.toString()); - resolve(JSON.parse(retJSON)); - }); - }); - request.on("error", function (error) { - console.log("SLACK API GET ERROR : ", JSON.stringify(error)); - console.log("SLACK API GET ERROR Code : ", JSON.stringify(error.code)); - console.log("SLACK API GET ERROR Message : ", JSON.stringify(error.message)); - console.log("SLACK API GET ERROR Stack : ", JSON.stringify(error.stack)); - reject(error); - }); - request.end(); - if(log) console.log("SLACK API GET - Request Results : ", request); - }); -} - - - - - -exports.processApiMsg = processApiMsg; -async function processApiMsg(data) { - // ==#####== VERIFY TO UPLOAD ==#####== - if(log) console.log("PROCESS API MESSAGE - Data : ", data); - let message = {}; - (data.token) ? message.token = data.token : null ; - (data.delete_original) ? message.delete_original = data.delete_original : message.delete_original = false ; - (data.replace_original) ? message.replace_original = data.replace_original : message.replace_original = false ; - (data.response_type) ? message.response_type = data.response_type : message.response_type = 'in_channel' ; // 'ephemeral' - (data.thread_ts) ? message.thread_ts = data.thread_ts : null ; - (data.return_im) ? message.return_im = data.return_im : null ; - (data.channel) ? message.channel = data.channel : null ; - (data.users) ? message.users = data.users : null ; - (data.trigger_id) ? message.trigger_id = data.trigger_id : null ; - (data.text) ? message.text = data.text : null ; - (data.blocks) ? message.blocks = data.blocks : null ; - (data.view) ? message.view = data.view : null ; - if(log) console.log("PROCESS API MESSAGE - Message : ", message); - - let msgData = {}; - (data.b_token) ? msgData.b_token = data.b_token : null ; - (data.api_method) ? msgData.api_method = data.api_method : null ; - (data.response_url) ? msgData.response_url = data.response_url : null ; - (data.trigger_id) ? msgData.trigger_id = data.trigger_id : null ; // HERE? - if(log) console.log("PROCESS API MESSAGE - Message Data: ", msgData); - - let result = await slackApiPost(message, msgData); - return result; -} - - - - -exports.slackApiPost = slackApiPost; -async function slackApiPost(message, data){ - // ==#####== SLACK API POST ==#####=== - if(log) console.log("SLACK API POST - Message : ", message); - if(log) console.log("SLACK API POST - Data : ", data); - var url; - if((data.response_url) && (data.response_url.length > 0)){ - url = data.response_url; - } else { - url = "https://slack.com/api/" + data.api_method; - } - if(log) console.log("SLACK API POST - URL : ", url); - var options = { - method: "POST", - port: 443, - headers: { - "Authorization": "Bearer " + data.b_token, - "Content-Type": "application/json; charset=utf-8", - "Content-Length": JSON.stringify(message).length - } - }; - if(log) console.log("SLACK API POST - Request Options : ", options); - let retJSON = ''; - return new Promise(function(resolve, reject) { - const request = HTTPS.request(url, options, function(res) { - if(log) console.log('SLACK API POST - Request Response', res); - if(log) console.log('SLACK API POST - Request Response Status Code:', res.statusCode); - if(log) console.log('SLACK API POST - Request Response Headers:', res.headers); - res.setEncoding( 'utf8' ); - res.on("data", function(chunk){ /* console.log("Chunk = ", chunk); */ retJSON += chunk; }); - res.on("end", function () { - if (typeof(retJSON) === "string") { - try { - // ==#####== STRINGIFIED RESPONSE ==#####== - if(log) console.log("SLACK API POST - Response Results", retJSON); - resolve(JSON.parse(retJSON)) ; - } catch(err) { - // ==#####== NATIVE JSON RESPONSE ==#####== - if(log) console.log("SLACK API POST - Response Results", JSON.stringify(retJSON)); - resolve(retJSON) ; - } - } else { - resolve(retJSON) ; - } - }); - }); - request.on("error", function (error) { - console.log("SLACK API POST ERROR : ", JSON.stringify(error)); - console.log("SLACK API POST ERROR Code : ", JSON.stringify(error.code)); - console.log("SLACK API POST ERROR Message : ", JSON.stringify(error.message)); - console.log("SLACK API POST ERROR Stack : ", JSON.stringify(error.stack)); - reject(error); - }); - request.write(JSON.stringify(message)); - request.end(); - if(log) console.log("SLACK API POST - Request Results : ", request); - }); -} - - - - - -exports.getTimeZoneOptions = getTimeZoneOptions; -async function getTimeZoneOptions(){ - // https://github.com/raw/dmfilipenko/timezones.json/master/timezones.json - // https://gist.github.com/jaredreich/9f4aa2a1d460100aa40ddbc279d85660#file-timezones-json - var timezones = require("./timezones.json"); - var items = []; - for (var i = 0; i < timezones.length; i++) { - var item = {}; - // item.text = timezones[i].value; - // item.value = timezones[i].offset; - // var item = { text: timezones[i].value , value: timezones[i].offset }; - item.text = { "type": "plain_text", "text": timezones[i].text } ; - item.value = timezones[i].value.toString(); - items.push(item); - } - if(log) console.log("GET TIME ZONE OPTIONS : ", items); - return items; -} \ No newline at end of file From 25434d8dfe5d5177c982d19e3f8433a9b20b59cf Mon Sep 17 00:00:00 2001 From: Brian Whaley Date: Thu, 28 May 2020 16:31:14 -0400 Subject: [PATCH 5/7] v3.0.0 v3.0.0 --- README.md | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 129d3b6..ff8e0bd 100644 --- a/README.md +++ b/README.md @@ -1 +1,47 @@ -# nerdjokes.com \ No newline at end of file + +## About NerdJokes +View random science, technology, math, and nerd jokes, or schedule them to be delivered to specific channels in your workspace! + +Type '/nerdjokes' to get a random joke to immediately share with your teammates. +Type '/nerdjokes help' to get information about nerdjokes slash commands. +Type '/nerdjokes bug' to get more information about submitting a bug. +Type '/nerdjokes support' to get more information on how to reach out for support. +Type '/nerdjokes getjoke' to get a random joke sent immediately. +Type '/nerdjokes addjoke' to recommend a new joke to be added to the collection. +Type '/nerdjokes addschedule' to add or edit a schedule for delivering jokes to the current channel. +Type '/nerdjokes deleteschedule' to delete a schedule for delivering jokes to the current channel. +Type '/nerdjokes version' to to see what version of NerdJokes you are using. + +### STEP 1: RANDOM JOKE ON DEMAND +Type '/nerdjokes' or '/nerdjokes getjoke' to get a random joke to immediately share with your teammates. + +### STEP 2: CREATE OR DELETE A SCHEDULE +Type '/nerdjokes addschedule' to add or edit a schedule for delivering jokes to the current channel. +Type '/nerdjokes deleteschedule' to delete a schedule for delivering jokes to the current channel. + +### STEP 3: STEP 3: ADD A JOKE +Type '/nerdjokes addjoke' to recommend a new joke to be added to the collection. + +### STEP 4: HELP +Type '/nerdjokes help' to get information about nerdjokes slash commands. +Type '/nerdjokes bug' to get more information about submitting a bug. +Type '/nerdjokes support' to get more information on how to reach out for support. +Type '/nerdjokes version' to to see what version of NerdJokes you are using. + +## How it Works +NerdJokes uses all AWS services to implement its features. +API Gateway to manage load and broker requests and responses +Lambda for executing code in a serverless environment +DynamoDB for jokes, settings, and token storage + +## Learn More +[NerdJokes on Pixelated](https://pixelated.tech/nerdjokes.html) + +### Install Stkr +[NerdJokes on Pixelated](https://pixelated.tech/nerdjokes.html) + +### Terms of Service +[NerdJokes on Pixelated](https://pixelated.tech/nerdjokes.html) + +### Privacy Policy +[NerdJokes on Pixelated](https://pixelated.tech/nerdjokes.html) From c80389a6bff7a35dfee7a375d71e1e9140d119b1 Mon Sep 17 00:00:00 2001 From: Brian Whaley Date: Thu, 28 May 2020 16:52:36 -0400 Subject: [PATCH 6/7] v3.0.0 v3.0.0 --- README.md | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index ff8e0bd..43db86d 100644 --- a/README.md +++ b/README.md @@ -2,37 +2,37 @@ ## About NerdJokes View random science, technology, math, and nerd jokes, or schedule them to be delivered to specific channels in your workspace! -Type '/nerdjokes' to get a random joke to immediately share with your teammates. -Type '/nerdjokes help' to get information about nerdjokes slash commands. -Type '/nerdjokes bug' to get more information about submitting a bug. -Type '/nerdjokes support' to get more information on how to reach out for support. -Type '/nerdjokes getjoke' to get a random joke sent immediately. -Type '/nerdjokes addjoke' to recommend a new joke to be added to the collection. -Type '/nerdjokes addschedule' to add or edit a schedule for delivering jokes to the current channel. -Type '/nerdjokes deleteschedule' to delete a schedule for delivering jokes to the current channel. -Type '/nerdjokes version' to to see what version of NerdJokes you are using. +* Type '/nerdjokes' to get a random joke to immediately share with your teammates. +* Type '/nerdjokes help' to get information about nerdjokes slash commands. +* Type '/nerdjokes bug' to get more information about submitting a bug. +* Type '/nerdjokes support' to get more information on how to reach out for support. +* Type '/nerdjokes getjoke' to get a random joke sent immediately. +* Type '/nerdjokes addjoke' to recommend a new joke to be added to the collection. +* Type '/nerdjokes addschedule' to add or edit a schedule for delivering jokes to the current channel. +* Type '/nerdjokes deleteschedule' to delete a schedule for delivering jokes to the current channel. +* Type '/nerdjokes version' to to see what version of NerdJokes you are using. ### STEP 1: RANDOM JOKE ON DEMAND -Type '/nerdjokes' or '/nerdjokes getjoke' to get a random joke to immediately share with your teammates. +* Type '/nerdjokes' or '/nerdjokes getjoke' to get a random joke to immediately share with your teammates. ### STEP 2: CREATE OR DELETE A SCHEDULE -Type '/nerdjokes addschedule' to add or edit a schedule for delivering jokes to the current channel. -Type '/nerdjokes deleteschedule' to delete a schedule for delivering jokes to the current channel. +* Type '/nerdjokes addschedule' to add or edit a schedule for delivering jokes to the current channel. +* Type '/nerdjokes deleteschedule' to delete a schedule for delivering jokes to the current channel. ### STEP 3: STEP 3: ADD A JOKE -Type '/nerdjokes addjoke' to recommend a new joke to be added to the collection. +* Type '/nerdjokes addjoke' to recommend a new joke to be added to the collection. ### STEP 4: HELP -Type '/nerdjokes help' to get information about nerdjokes slash commands. -Type '/nerdjokes bug' to get more information about submitting a bug. -Type '/nerdjokes support' to get more information on how to reach out for support. -Type '/nerdjokes version' to to see what version of NerdJokes you are using. +* Type '/nerdjokes help' to get information about nerdjokes slash commands. +* Type '/nerdjokes bug' to get more information about submitting a bug. +* Type '/nerdjokes support' to get more information on how to reach out for support. +* Type '/nerdjokes version' to to see what version of NerdJokes you are using. ## How it Works -NerdJokes uses all AWS services to implement its features. -API Gateway to manage load and broker requests and responses -Lambda for executing code in a serverless environment -DynamoDB for jokes, settings, and token storage +* NerdJokes uses all AWS services to implement its features. +* API Gateway to manage load and broker requests and responses +* Lambda for executing code in a serverless environment +* DynamoDB for jokes, settings, and token storage ## Learn More [NerdJokes on Pixelated](https://pixelated.tech/nerdjokes.html) From b5a8c98588318570bb8609d27c1df3e1db863b40 Mon Sep 17 00:00:00 2001 From: Brian Whaley Date: Fri, 29 May 2020 00:45:49 -0400 Subject: [PATCH 7/7] v3.0.0 v3.0.0 --- nerdjokes.yaml | 4 ++-- schedule.js | 13 ++++++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/nerdjokes.yaml b/nerdjokes.yaml index c562252..053f0db 100644 --- a/nerdjokes.yaml +++ b/nerdjokes.yaml @@ -10,13 +10,13 @@ Resources: CodeUri: . Description: '' MemorySize: 256 - Timeout: 30 + Timeout: 6 Role: 'arn:aws:iam::971579260814:role/tech-jokes-role' Events: Schedule1: Type: Schedule Properties: - Schedule: cron(0 0-23 * * ? *) + Schedule: cron(0-59 0-23 * * ? *) Api1: Type: Api Properties: diff --git a/schedule.js b/schedule.js index 623818b..f0ba43e 100644 --- a/schedule.js +++ b/schedule.js @@ -189,7 +189,7 @@ module.exports = class Schedule { var items = []; for (var i = 0; i < 24; i++) { var item = {}; - var hour = (i > 12) ? (i - 12 + ":00pm") : (((i==0) ? 12 : i ) + ":00am"); + var hour = (i < 12) ? (((i==0) ? 12 : i ) + ":00am") : (((i==12) ? 12 : i - 12 ) + ":00pm") ; item.text = { "type": "plain_text", "text": hour } ; item.value = i.toString(); items.push(item); @@ -467,20 +467,23 @@ module.exports = class Schedule { "token": myschedule.b_token, "team_id": myschedule.team_id, "channel_id": myschedule.channel_id, - "command": "/nerdjokes", + "command": event.command, "text": "getjoke", } }; if(log) console.log("RUN SCHEDULE - Recursive Lambda - Payload : ", lambdaPayload); let lambdaParams = { - FunctionName: 'nerdjokes:' + event.env, - InvocationType: 'Event', + FunctionName: process.env.AWS_LAMBDA_FUNCTION_NAME + ':' + event.env, + // ==#####== Event == Async, RequestResponse == Sync ==#####== + InvocationType: 'Event', Payload: JSON.stringify(lambdaPayload) }; if(log) console.log("RUN SCHEDULE - Recursive Lambda - Params : ", lambdaParams); var lambda = new AWS.LAMBDA(); - lambda.callLambda(lambdaParams); + await lambda.callLambda(lambdaParams); + // ==#####== RESPONSE 200 FOR EVENT ==#####== } + return null; } }; \ No newline at end of file