From c0577d9be1f6e29b8f8c4a362a7714de84a3c8d4 Mon Sep 17 00:00:00 2001 From: Carter Medlin Date: Thu, 4 Nov 2021 13:07:46 -0700 Subject: [PATCH 1/5] working on new module --- WordpressSync/endpoints.json | 4 +- WordpressSync/index.js | 53 ++--- common/slackBot/index.js | 369 +++++++++++++++++++---------------- 3 files changed, 227 insertions(+), 199 deletions(-) diff --git a/WordpressSync/endpoints.json b/WordpressSync/endpoints.json index 5134e4b..4cb5241 100644 --- a/WordpressSync/endpoints.json +++ b/WordpressSync/endpoints.json @@ -10,7 +10,7 @@ "name": "Covid 19 website", "description": "Using data to create test output.", "enabled": false, - "enabledLocal": false, + "enabledLocal": true, "ReportingChannel_Slack": "C01H6RB99E2", "WordPressSource": { "url": "https://as-go-covid19-d-001.azurewebsites.net", @@ -27,7 +27,7 @@ "name": "Covid 19 website Staging", "description": "(Staging) Using data to create test output.", "enabled": false, - "enabledLocal": false, + "enabledLocal": true, "ReportingChannel_Slack": "C01H6RB99E2", "WordPressSource": { "url": "https://as-go-covid19-d-001.azurewebsites.net", diff --git a/WordpressSync/index.js b/WordpressSync/index.js index 61f48ff..65457e4 100644 --- a/WordpressSync/index.js +++ b/WordpressSync/index.js @@ -5,13 +5,9 @@ const { GitHubCredentials, SourceEndpointConfigData } = require("@cagov/wordpress-to-github/common"); -const { - slackBotReportError, - slackBotChatPost, - slackBotReplyPost -} = require("../common/slackBot"); -const debugChannel = "C01DBP67MSQ"; // #testingbot -//const debugChannel = 'C01H6RB99E2'; //Carter debug +const SlackBot = require("../common/slackBot"); +//const debugChannel = "C01DBP67MSQ"; // #testingbot +const debugChannel = "C01H6RB99E2"; //Carter debug const endPointsJson = require("./endpoints.json"); /** @type {SourceEndpointConfigData[]} */ const endpoints = endPointsJson.data.projects; @@ -25,6 +21,20 @@ const gitHubCredentials = { token: `${process.env["GITHUB_TOKEN"]}` }; +const slackBotGetToken = () => { + const token = process.env["SLACKBOT_TOKEN"]; + + if (!token) { + //developers that don't set the creds can still use the rest of the code + console.error( + `You need local.settings.json to contain "SLACKBOT_TOKEN" to use slackbot features.` + ); + return; + } + + return token; +}; + /** * * @param {{executionContext:{functionName:string}}} context @@ -51,13 +61,8 @@ module.exports = async function (context, myTimer, activeEndpoints) { try { await doProcessEndpoints(work); } catch (e) { - await slackBotReportError( - debugChannel, - `Error running ${appName}`, - e, - context, - myTimer - ); + const slackBot = new SlackBot(slackBotGetToken(), debugChannel); + await slackBot.Error(e, myTimer); } }; @@ -84,7 +89,7 @@ const doProcessEndpoints = async work => { gitHubCommitter ); - if (endpoint.ReportingChannel_Slack) { + if (endpoint.ReportingChannel_Slack && slackBotGetToken()) { //Endpoint reporting channel enabled. Add a post for each commit report. if (commitReports?.length) { /** @type {string[]} */ @@ -97,25 +102,21 @@ const doProcessEndpoints = async work => { ); }); + const slackBot = new SlackBot( + slackBotGetToken(), + endpoint.ReportingChannel_Slack + ); + const allfileNames = [...new Set(mergeFileNames)]; - const slackPostTS = ( - await ( - await slackBotChatPost( - endpoint.ReportingChannel_Slack, - `${endpoint.name} - _${allfileNames.join(", ")}_` - ) - ).json() - ).ts; + await slackBot.Chat(`${endpoint.name} - _${allfileNames.join(", ")}_`); for (const commitReport of commitReports) { const fileData = commitReport.Files.map( x => `• ${x.status} - _${x.filename.split("/").slice(-1)[0]}_` ).join("\n"); - await slackBotReplyPost( - endpoint.ReportingChannel_Slack, - slackPostTS, + await slackBot.Reply( `<${commitReport.Commit.html_url}|${commitReport.Commit.message}>\n${fileData}` ); } diff --git a/common/slackBot/index.js b/common/slackBot/index.js index a7ee528..9f5e654 100644 --- a/common/slackBot/index.js +++ b/common/slackBot/index.js @@ -1,5 +1,5 @@ -// @ts-check -const fetchRetry = require("fetch-retry")(require("node-fetch/lib"), { +//@ts-check +const fetch = require("fetch-retry")(require("node-fetch/lib"), { retries: 3, retryDelay: 2000 }); @@ -11,187 +11,214 @@ const slackApiReaction = "https://slack.com/api/reactions.add"; //For help building attachments...go here... //https://api.slack.com/docs/messages/builder -const slackBotGetToken = () => { - const token = process.env["SLACKBOT_TOKEN"]; - - if (!token) { - //developers that don't set the creds can still use the rest of the code - console.error( - `You need local.settings.json to contain "SLACKBOT_TOKEN" to use slackbot features.` - ); - return; - } - - return token; -}; - -const slackApiHeaders = { - Authorization: `Bearer ${slackBotGetToken()}`, - "Content-Type": "application/json;charset=utf-8" -}; - -const slackApiPost = bodyJSON => ({ - method: "POST", - headers: slackApiHeaders, - body: JSON.stringify(bodyJSON) -}); -const slackApiGet = () => ({ - headers: slackApiHeaders -}); - -/** - * List the post history for a channel - * - * (See https://api.slack.com/methods/conversations.history) - * - * @param {string} channel - Slack channel to search in - */ -const slackBotChannelHistory = async channel => - fetchRetry(`${slackApiChannelHistory}?channel=${channel}`, slackApiGet()); - /** - * Get a list of replies for a post - * - * (See https://api.slack.com/methods/conversations.replies) - * - * @param {string} channel - Slack channel to search in - * @param {string} ts - Timestamp (TS) for root Slack post + * @typedef {object} slackBotChatOptions + * @property {boolean} [as_user] Pass true to post the message as the authed user, instead of as a bot. Defaults to false. + * @property {*[]} [attachments] A JSON-based array of structured attachments, presented as a URL-encoded string. + * @property {*[]} [blocks] A JSON-based array of structured blocks, presented as a URL-encoded string. + * @property {string} [icon_emoji] Emoji to use as the icon for this message. Overrides icon_url. Must be used in conjunction with as_user set to false, otherwise ignored. + * @property {string} [icon_url] URL to an image to use as the icon for this message. Must be used in conjunction with as_user set to false, otherwise ignored. + * @property {boolean} [link_names] Find and link channel names and usernames. + * @property {boolean} [mrkdwn] Disable Slack markup parsing by setting to false. Enabled by default. Default: true + * @property {string} [parse] Change how messages are treated. Defaults to none. + * @property {boolean} [reply_broadcast] Used in conjunction with thread_ts and indicates whether reply should be made visible to everyone in the channel or conversation. Defaults to false. + * @property {boolean} [unfurl_links] Pass true to enable unfurling of primarily text-based content. + * @property {boolean} [unfurl_media] Pass false to disable unfurling of media content. + * @property {string} [username] Set your bot's user name. Must be used in conjunction with as_user set to false, otherwise ignored. */ -const slackBotChannelReplies = async (channel, ts) => - fetchRetry( - `${slackApiChannelReplies}?channel=${channel}&ts=${ts}`, - slackApiGet() - ); /** - * Add a Slack post - * - * (See https://api.slack.com/methods/chat.postMessage) - * - * (Also https://api.slack.com/docs/messages/builder) - * - * @param {string} channel - Slack channel to post in - * @param {string} text - Post text - * @param {string} [attachments] - Optional Post attachments + * @typedef {object} slackChatResultMessage + * @property {string} bot_id + * @property {{app_id:string,deleted:boolean,icons:*,id:string,name:string,team_id:string,updated:number}} [bot_profile] + * @property {{emoji:string,image_64:string}} [icons] + * @property {string} [subtype] + * @property {string} [team] + * @property {string} text + * @property {string} [thread_ts] + * @property {string} ts + * @property {string} type + * @property {string} [username] + * @property {string} [user] */ -const slackBotChatPost = async (channel, text, attachments) => { - const payload = { - channel, - text, - attachments - }; - return fetchRetry(slackApiChatPost, slackApiPost(payload)); -}; -/** - * Add a reply to a Slack post. - * - * @param {string} channel - Slack channel to post in - * @param {string} thread_ts - Timestamp (TS) for Slack post - * @param {string} text - Post text - * @param {string} [attachments] - Optional Post attachments - */ -const slackBotReplyPost = async (channel, thread_ts, text, attachments) => { - const payload = { - channel, - text, - thread_ts, - attachments - }; - - return fetchRetry(slackApiChatPost, slackApiPost(payload)); -}; /** - * Add a reaction to a Slack post.
- * - * (see https://api.slack.com/methods/reactions.add) - * - * @param {string} channel - Slack channel to post in - * @param {string} timestamp - Timestamp (TS) for Slack post - * @param {string} name - emoji name + * @typedef {object} slackChatResult + * @property {boolean} ok + * @property {string} ts + * @property {string} channel + * @property {slackChatResultMessage} message */ -const slackBotReactionAdd = async (channel, timestamp, name) => { - const payload = { - channel, - timestamp, - name - }; - - return fetchRetry(slackApiReaction, slackApiPost(payload)); -}; - -const slackBotDelayedChatPost = async (channel, text, post_at) => { - const payload = { - channel, - text, - post_at - }; - - const fetchResp = await fetchRetry( - "https://slack.com/api/chat.scheduleMessage", - slackApiPost(payload) - ); - const postInfo = await fetchResp.json(); - return postInfo; -}; /** - * Report an error to a slack channel. - * - * @param {string} channel - Slack channel to post in - * @param {string} title - the post title - * @param {{stack:string}} errorObject - the error object to display - * @param {*} [request] - optional request object to display - * @param {*} [data] - optional data object to display + * Checks for a slack response json and throws an error if it finds one + * @param {Response} response fetch response */ -const slackBotReportError = async ( - channel, - title, - errorObject, - request, - data -) => { - console.error(errorObject); - - let slackText = `${title}\n*Error Stack*\n\`\`\`${errorObject.stack}\`\`\``; - - if (request) { - slackText += `\n\n*Request*\n\`\`\`${JSON.stringify( - request, - null, - 2 - )}\`\`\``; - } - if (data) { - slackText += `\n\n*Data*\n\`\`\`${JSON.stringify(data, null, 2)}\`\`\``; - } - - const historyResponse = await slackBotChannelHistory(channel); - const history = await historyResponse.json(); - const lastHourHistory = history.messages.filter( - c => - c.text.startsWith(`${title}\n`) && - // @ts-ignore - // prettier-ignore - (new Date() - new Date(1000 * Number(c.latest_reply || c.ts))) / 1000 / 60 / 60 < 1 - ); //last hour - //check to see if the last post was the same title, if so make this a reply - - if (lastHourHistory && lastHourHistory.length) { - //add to error thread - return slackBotReplyPost(channel, lastHourHistory[0].ts, slackText); +const getSlackJsonResponse = async response => { + if (response.ok) { + const json = await response.json(); + if (json.ok) { + return json; + } else { + throw new Error(`Slack Error : ${JSON.stringify(json, null, 2)}`); + } } else { - //new error - return slackBotChatPost(channel, slackText); + throw new Error( + `Slack Connection Error : ${response.status} - ${response.statusText}` + ); } }; -module.exports = { - slackBotChatPost, - slackBotReplyPost, - slackBotDelayedChatPost, - slackBotReportError, - slackBotChannelHistory, - slackBotChannelReplies, - slackBotReactionAdd -}; +class slackBot { + /** + * @param {string} token + * @param {string} channel + * @param {slackBotChatOptions} [defaultOptions] + */ + constructor(token, channel, defaultOptions) { + /** @type {string} */ + this.channel = channel; + /** @type {string} */ + this.token = token; + /** @type {slackBotChatOptions} */ + this.defaultOptions = defaultOptions; + + /** @type {string} */ + this.thread_ts = ""; + + /** + * API Headers with token + */ + this.slackApiHeaders = () => ({ + Authorization: `Bearer ${this.token}`, + "Content-Type": "application/json;charset=utf-8" + }); + + /** + * fetch settings for an API POST + * @param {*} bodyJSON JSON to POST + */ + this.slackApiPost = bodyJSON => ({ + method: "POST", + headers: this.slackApiHeaders(), + body: JSON.stringify(bodyJSON) + }); + + /** + * fetch settings for an API GET + */ + this.slackApiGet = () => ({ + headers: this.slackApiHeaders() + }); + + /** + * Add a Slack post + * + * (See https://api.slack.com/methods/chat.postMessage) + * + * (Also https://api.slack.com/docs/messages/builder) + * @param {string} text + * @param {slackBotChatOptions} [options] - Optional options + */ + this.Chat = async (text, options) => { + const payload = { + channel: this.channel, + text, + ...defaultOptions, + ...options + }; + + const response = await fetch( + slackApiChatPost, + this.slackApiPost(payload) + ); + /** @type {slackChatResult} */ + const json = await getSlackJsonResponse(response); + this.thread_ts = json.ts; + return json; + }; + + /** + * Add a reply to the last Slack post. + * @param {string} text + * @param {slackBotChatOptions} [options] - Optional options + */ + this.Reply = async (text, options) => { + if (this.thread_ts) { + const payload = { + channel: this.channel, + text, + thread_ts: this.thread_ts, + ...defaultOptions, + ...options + }; + const response = await fetch( + slackApiChatPost, + this.slackApiPost(payload) + ); + + /** @type {slackChatResult} */ + const json = await getSlackJsonResponse(response); + this.thread_ts = json.ts; + return json; + } else { + return await this.Chat(text, options); + } + }; + + /** + * Send error details to the channel + * @param {Error} e The Error object that was caught + * @param {*} [data] Any data that should be added to the exception log + * @param {slackBotChatOptions} [options] - Optional options + */ + this.Error = async (e, data, options) => { + if (!this.thread_ts) { + await this.Chat(`Error - _${e.message}_`, options); + } + + let message = `*Error Stack*\n\`\`\`${e.stack}\`\`\``; + if (data) { + message += `\n*Data*\n\`\`\`${JSON.stringify(data, null, 2)}\`\`\``; + } + + return await this.Reply(message, options); + }; + + /** + * Add a reaction to the last Slack post.
+ * + * (see https://api.slack.com/methods/reactions.add) + * + * @param {string} name - emoji name + */ + this.ReactionAdd = async name => { + const payload = { + channel: this.channel, + timestamp: this.thread_ts, + name + }; + const response = await fetch( + slackApiReaction, + this.slackApiPost(payload) + ); + return await getSlackJsonResponse(response); + }; + + /** + * Returns a clone. Usefull for keeping track of an original timestamp + */ + this.Clone = () => { + const copied = new slackBot( + this.token, + this.channel, + this.defaultOptions + ); + copied.thread_ts = this.thread_ts; + + return copied; + }; + } +} + +module.exports = slackBot; From d9ba3f90d50617f7952c0cd29558abda04672ae2 Mon Sep 17 00:00:00 2001 From: Carter Medlin Date: Thu, 4 Nov 2021 13:22:25 -0700 Subject: [PATCH 2/5] ready for new slackbot --- WordpressSync/index.js | 9 +++++---- common/slackBot/index.js | 9 +++++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/WordpressSync/index.js b/WordpressSync/index.js index 65457e4..4e453de 100644 --- a/WordpressSync/index.js +++ b/WordpressSync/index.js @@ -6,8 +6,8 @@ const { SourceEndpointConfigData } = require("@cagov/wordpress-to-github/common"); const SlackBot = require("../common/slackBot"); -//const debugChannel = "C01DBP67MSQ"; // #testingbot -const debugChannel = "C01H6RB99E2"; //Carter debug +const debugChannel = "C01DBP67MSQ"; // #testingbot +//const debugChannel = "C01H6RB99E2"; //Carter debug const endPointsJson = require("./endpoints.json"); /** @type {SourceEndpointConfigData[]} */ const endpoints = endPointsJson.data.projects; @@ -104,12 +104,13 @@ const doProcessEndpoints = async work => { const slackBot = new SlackBot( slackBotGetToken(), - endpoint.ReportingChannel_Slack + endpoint.ReportingChannel_Slack, + { username: endpoint.name } ); const allfileNames = [...new Set(mergeFileNames)]; - await slackBot.Chat(`${endpoint.name} - _${allfileNames.join(", ")}_`); + await slackBot.Chat(`_${allfileNames.join(", ")}_`); for (const commitReport of commitReports) { const fileData = commitReport.Files.map( diff --git a/common/slackBot/index.js b/common/slackBot/index.js index 9f5e654..33af3ce 100644 --- a/common/slackBot/index.js +++ b/common/slackBot/index.js @@ -83,6 +83,8 @@ class slackBot { /** @type {slackBotChatOptions} */ this.defaultOptions = defaultOptions; + /** @type {string} */ + this.ts = ""; /** @type {string} */ this.thread_ts = ""; @@ -135,6 +137,7 @@ class slackBot { /** @type {slackChatResult} */ const json = await getSlackJsonResponse(response); this.thread_ts = json.ts; + this.ts = json.ts; return json; }; @@ -159,7 +162,8 @@ class slackBot { /** @type {slackChatResult} */ const json = await getSlackJsonResponse(response); - this.thread_ts = json.ts; + this.ts = json.ts; + this.thread_ts = json.message.thread_ts; return json; } else { return await this.Chat(text, options); @@ -195,7 +199,7 @@ class slackBot { this.ReactionAdd = async name => { const payload = { channel: this.channel, - timestamp: this.thread_ts, + timestamp: this.ts, name }; const response = await fetch( @@ -215,6 +219,7 @@ class slackBot { this.defaultOptions ); copied.thread_ts = this.thread_ts; + copied.ts = this.ts; return copied; }; From 6d4bb98c6f6b758494a6779af99e5a94d16401ef Mon Sep 17 00:00:00 2001 From: Carter Medlin Date: Thu, 4 Nov 2021 13:23:07 -0700 Subject: [PATCH 3/5] delta reduce --- WordpressSync/endpoints.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WordpressSync/endpoints.json b/WordpressSync/endpoints.json index 4cb5241..5134e4b 100644 --- a/WordpressSync/endpoints.json +++ b/WordpressSync/endpoints.json @@ -10,7 +10,7 @@ "name": "Covid 19 website", "description": "Using data to create test output.", "enabled": false, - "enabledLocal": true, + "enabledLocal": false, "ReportingChannel_Slack": "C01H6RB99E2", "WordPressSource": { "url": "https://as-go-covid19-d-001.azurewebsites.net", @@ -27,7 +27,7 @@ "name": "Covid 19 website Staging", "description": "(Staging) Using data to create test output.", "enabled": false, - "enabledLocal": true, + "enabledLocal": false, "ReportingChannel_Slack": "C01H6RB99E2", "WordPressSource": { "url": "https://as-go-covid19-d-001.azurewebsites.net", From 0a2e354ebfd148abe38a60fa20b408d3bc202476 Mon Sep 17 00:00:00 2001 From: Carter Medlin Date: Thu, 4 Nov 2021 13:23:36 -0700 Subject: [PATCH 4/5] delta reduce --- WordpressSync/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordpressSync/index.js b/WordpressSync/index.js index 4e453de..227dfda 100644 --- a/WordpressSync/index.js +++ b/WordpressSync/index.js @@ -7,7 +7,7 @@ const { } = require("@cagov/wordpress-to-github/common"); const SlackBot = require("../common/slackBot"); const debugChannel = "C01DBP67MSQ"; // #testingbot -//const debugChannel = "C01H6RB99E2"; //Carter debug +//const debugChannel = 'C01H6RB99E2'; //Carter debug const endPointsJson = require("./endpoints.json"); /** @type {SourceEndpointConfigData[]} */ const endpoints = endPointsJson.data.projects; From 24b560f952151c3d19e4c65d90d3e8a1851fc87a Mon Sep 17 00:00:00 2001 From: Carter Medlin Date: Thu, 4 Nov 2021 16:06:04 -0700 Subject: [PATCH 5/5] NPM Module! --- WordpressSync/index.js | 2 +- common/slackBot/index.js | 229 --------------------------------------- package-lock.json | 33 ++++-- package.json | 1 + 4 files changed, 23 insertions(+), 242 deletions(-) delete mode 100644 common/slackBot/index.js diff --git a/WordpressSync/index.js b/WordpressSync/index.js index 227dfda..23fdd58 100644 --- a/WordpressSync/index.js +++ b/WordpressSync/index.js @@ -5,7 +5,7 @@ const { GitHubCredentials, SourceEndpointConfigData } = require("@cagov/wordpress-to-github/common"); -const SlackBot = require("../common/slackBot"); +const SlackBot = require("@cagov/slack-connector"); const debugChannel = "C01DBP67MSQ"; // #testingbot //const debugChannel = 'C01H6RB99E2'; //Carter debug const endPointsJson = require("./endpoints.json"); diff --git a/common/slackBot/index.js b/common/slackBot/index.js deleted file mode 100644 index 33af3ce..0000000 --- a/common/slackBot/index.js +++ /dev/null @@ -1,229 +0,0 @@ -//@ts-check -const fetch = require("fetch-retry")(require("node-fetch/lib"), { - retries: 3, - retryDelay: 2000 -}); -const slackApiChatPost = "https://slack.com/api/chat.postMessage"; -const slackApiChannelHistory = "https://slack.com/api/conversations.history"; -const slackApiChannelReplies = "https://slack.com/api/conversations.replies"; -const slackApiReaction = "https://slack.com/api/reactions.add"; - -//For help building attachments...go here... -//https://api.slack.com/docs/messages/builder - -/** - * @typedef {object} slackBotChatOptions - * @property {boolean} [as_user] Pass true to post the message as the authed user, instead of as a bot. Defaults to false. - * @property {*[]} [attachments] A JSON-based array of structured attachments, presented as a URL-encoded string. - * @property {*[]} [blocks] A JSON-based array of structured blocks, presented as a URL-encoded string. - * @property {string} [icon_emoji] Emoji to use as the icon for this message. Overrides icon_url. Must be used in conjunction with as_user set to false, otherwise ignored. - * @property {string} [icon_url] URL to an image to use as the icon for this message. Must be used in conjunction with as_user set to false, otherwise ignored. - * @property {boolean} [link_names] Find and link channel names and usernames. - * @property {boolean} [mrkdwn] Disable Slack markup parsing by setting to false. Enabled by default. Default: true - * @property {string} [parse] Change how messages are treated. Defaults to none. - * @property {boolean} [reply_broadcast] Used in conjunction with thread_ts and indicates whether reply should be made visible to everyone in the channel or conversation. Defaults to false. - * @property {boolean} [unfurl_links] Pass true to enable unfurling of primarily text-based content. - * @property {boolean} [unfurl_media] Pass false to disable unfurling of media content. - * @property {string} [username] Set your bot's user name. Must be used in conjunction with as_user set to false, otherwise ignored. - */ - -/** - * @typedef {object} slackChatResultMessage - * @property {string} bot_id - * @property {{app_id:string,deleted:boolean,icons:*,id:string,name:string,team_id:string,updated:number}} [bot_profile] - * @property {{emoji:string,image_64:string}} [icons] - * @property {string} [subtype] - * @property {string} [team] - * @property {string} text - * @property {string} [thread_ts] - * @property {string} ts - * @property {string} type - * @property {string} [username] - * @property {string} [user] - */ - -/** - * @typedef {object} slackChatResult - * @property {boolean} ok - * @property {string} ts - * @property {string} channel - * @property {slackChatResultMessage} message - */ - -/** - * Checks for a slack response json and throws an error if it finds one - * @param {Response} response fetch response - */ -const getSlackJsonResponse = async response => { - if (response.ok) { - const json = await response.json(); - if (json.ok) { - return json; - } else { - throw new Error(`Slack Error : ${JSON.stringify(json, null, 2)}`); - } - } else { - throw new Error( - `Slack Connection Error : ${response.status} - ${response.statusText}` - ); - } -}; - -class slackBot { - /** - * @param {string} token - * @param {string} channel - * @param {slackBotChatOptions} [defaultOptions] - */ - constructor(token, channel, defaultOptions) { - /** @type {string} */ - this.channel = channel; - /** @type {string} */ - this.token = token; - /** @type {slackBotChatOptions} */ - this.defaultOptions = defaultOptions; - - /** @type {string} */ - this.ts = ""; - /** @type {string} */ - this.thread_ts = ""; - - /** - * API Headers with token - */ - this.slackApiHeaders = () => ({ - Authorization: `Bearer ${this.token}`, - "Content-Type": "application/json;charset=utf-8" - }); - - /** - * fetch settings for an API POST - * @param {*} bodyJSON JSON to POST - */ - this.slackApiPost = bodyJSON => ({ - method: "POST", - headers: this.slackApiHeaders(), - body: JSON.stringify(bodyJSON) - }); - - /** - * fetch settings for an API GET - */ - this.slackApiGet = () => ({ - headers: this.slackApiHeaders() - }); - - /** - * Add a Slack post - * - * (See https://api.slack.com/methods/chat.postMessage) - * - * (Also https://api.slack.com/docs/messages/builder) - * @param {string} text - * @param {slackBotChatOptions} [options] - Optional options - */ - this.Chat = async (text, options) => { - const payload = { - channel: this.channel, - text, - ...defaultOptions, - ...options - }; - - const response = await fetch( - slackApiChatPost, - this.slackApiPost(payload) - ); - /** @type {slackChatResult} */ - const json = await getSlackJsonResponse(response); - this.thread_ts = json.ts; - this.ts = json.ts; - return json; - }; - - /** - * Add a reply to the last Slack post. - * @param {string} text - * @param {slackBotChatOptions} [options] - Optional options - */ - this.Reply = async (text, options) => { - if (this.thread_ts) { - const payload = { - channel: this.channel, - text, - thread_ts: this.thread_ts, - ...defaultOptions, - ...options - }; - const response = await fetch( - slackApiChatPost, - this.slackApiPost(payload) - ); - - /** @type {slackChatResult} */ - const json = await getSlackJsonResponse(response); - this.ts = json.ts; - this.thread_ts = json.message.thread_ts; - return json; - } else { - return await this.Chat(text, options); - } - }; - - /** - * Send error details to the channel - * @param {Error} e The Error object that was caught - * @param {*} [data] Any data that should be added to the exception log - * @param {slackBotChatOptions} [options] - Optional options - */ - this.Error = async (e, data, options) => { - if (!this.thread_ts) { - await this.Chat(`Error - _${e.message}_`, options); - } - - let message = `*Error Stack*\n\`\`\`${e.stack}\`\`\``; - if (data) { - message += `\n*Data*\n\`\`\`${JSON.stringify(data, null, 2)}\`\`\``; - } - - return await this.Reply(message, options); - }; - - /** - * Add a reaction to the last Slack post.
- * - * (see https://api.slack.com/methods/reactions.add) - * - * @param {string} name - emoji name - */ - this.ReactionAdd = async name => { - const payload = { - channel: this.channel, - timestamp: this.ts, - name - }; - const response = await fetch( - slackApiReaction, - this.slackApiPost(payload) - ); - return await getSlackJsonResponse(response); - }; - - /** - * Returns a clone. Usefull for keeping track of an original timestamp - */ - this.Clone = () => { - const copied = new slackBot( - this.token, - this.channel, - this.defaultOptions - ); - copied.thread_ts = this.thread_ts; - copied.ts = this.ts; - - return copied; - }; - } -} - -module.exports = slackBot; diff --git a/package-lock.json b/package-lock.json index 46354b4..76a0f4e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,8 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@cagov/wordpress-to-github": "^0.0.2" + "@cagov/slack-connector": "^0.0.1", + "@cagov/wordpress-to-github": "file:wordpress-to-github" }, "devDependencies": { "eslint": "^7.32.0", @@ -110,17 +111,19 @@ "node": ">=4" } }, - "node_modules/@cagov/wordpress-to-github": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/@cagov/wordpress-to-github/-/wordpress-to-github-0.0.2.tgz", - "integrity": "sha512-AVmIS2FWNKHTNjKbl+mofQ/H/S+NKSUnbg3+xgSlhh/gJOR7TCo7SN0A5HvgzEhz4w6gWuey5Mp8IHy5MEAkWA==", + "node_modules/@cagov/slack-connector": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@cagov/slack-connector/-/slack-connector-0.0.1.tgz", + "integrity": "sha512-YDJ4k9tg7JkWen90WlZzT+x+fG3Iiefn0/6YD8lHIX0mcueOrN/l7bjqFolSVFjTv+z4grXSfDGck78mEFBbRw==", "dependencies": { "fetch-retry": "^4.1.1", - "github-api": "^3.4.0", - "node-fetch": "^2.6.1", - "sha1": "^1.1.1" + "node-fetch": "^2.6.1" } }, + "node_modules/@cagov/wordpress-to-github": { + "resolved": "wordpress-to-github", + "link": true + }, "node_modules/@es-joy/jsdoccomment": { "version": "0.10.8", "integrity": "sha512-3P1JiGL4xaR9PoTKUHa2N/LKwa2/eUdRqGwijMWWgBqbFEqJUVpmaOi2TcjcemrsRMgFLBzQCK4ToPhrSVDiFQ==", @@ -1395,7 +1398,6 @@ "wordpress-to-github": { "name": "@cagov/wordpress-to-github", "version": "0.0.2", - "extraneous": true, "license": "MIT", "dependencies": { "fetch-retry": "^4.1.1", @@ -1480,10 +1482,17 @@ } } }, + "@cagov/slack-connector": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@cagov/slack-connector/-/slack-connector-0.0.1.tgz", + "integrity": "sha512-YDJ4k9tg7JkWen90WlZzT+x+fG3Iiefn0/6YD8lHIX0mcueOrN/l7bjqFolSVFjTv+z4grXSfDGck78mEFBbRw==", + "requires": { + "fetch-retry": "^4.1.1", + "node-fetch": "^2.6.1" + } + }, "@cagov/wordpress-to-github": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/@cagov/wordpress-to-github/-/wordpress-to-github-0.0.2.tgz", - "integrity": "sha512-AVmIS2FWNKHTNjKbl+mofQ/H/S+NKSUnbg3+xgSlhh/gJOR7TCo7SN0A5HvgzEhz4w6gWuey5Mp8IHy5MEAkWA==", + "version": "file:wordpress-to-github", "requires": { "fetch-retry": "^4.1.1", "github-api": "^3.4.0", diff --git a/package.json b/package.json index a27a7c1..71c856e 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "author": "", "license": "ISC", "dependencies": { + "@cagov/slack-connector": "^0.0.1", "@cagov/wordpress-to-github": "file:wordpress-to-github" }, "devDependencies": {