From 7f98f139901d6eeb9caf3159ed0dc5230fb13e93 Mon Sep 17 00:00:00 2001 From: Nick Funk Date: Tue, 21 Jul 2020 12:01:38 -0600 Subject: [PATCH] Send staff comments to Slack with all comments enabled (#3035) When we set up a Slack channel, you can select to have "All comments" sent to the slack channel. If we do this we want staff comments to come through as well. This wasn't happening before because there is a `staffCreated` flag instead of `created` when a staff member comments. So we change the trigger return type to an array, and when a staff comment appears, return `staffCreated` as well as `created` so the all filter lets it through. I also changed `getBlocks(...)` to `getContent(...)` as it now returns a JSON object with both a `blocks` array and a `text` string field. This fixes the problem where Slack would notifications would say "this content cannot be displayed". Now it will say the text provided. CORL-1172 --- .../events/listeners/slack/publishEvent.ts | 114 ++++++++++-------- .../server/events/listeners/slack/slack.ts | 10 +- 2 files changed, 69 insertions(+), 55 deletions(-) diff --git a/src/core/server/events/listeners/slack/publishEvent.ts b/src/core/server/events/listeners/slack/publishEvent.ts index feca9625a..6f4a660c6 100644 --- a/src/core/server/events/listeners/slack/publishEvent.ts +++ b/src/core/server/events/listeners/slack/publishEvent.ts @@ -14,7 +14,12 @@ import { GQLUSER_ROLE, } from "coral-server/graph/schema/__generated__/types"; -export type Trigger = "reported" | "pending" | "featured" | "created"; +export type Trigger = + | "reported" + | "pending" + | "featured" + | "created" + | "staffCreated"; export default class SlackPublishEvent { public comment: Comment; @@ -33,15 +38,15 @@ export default class SlackPublishEvent { this.author = author; } - private getTrigger(): Trigger | "staffCreated" | null { + private getTriggers(): (Trigger | null)[] { if ( this.actionType && this.actionType === "created" && this.authorIsStaff() ) { - return "staffCreated"; + return ["staffCreated", "created"]; } - return this.actionType; + return [this.actionType]; } private authorIsStaff() { @@ -56,34 +61,39 @@ export default class SlackPublishEvent { } public getMessage(): string { - switch (this.getTrigger()) { - case "created": - return "This comment has been created"; - case "staffCreated": - return "This comment has been created by staff"; - case "featured": - return "This comment has been featured"; - case "pending": - return "This comment is pending"; - case "reported": - return "This comment has been reported"; - default: - throw new Error("invalid trigger"); + const triggers = this.getTriggers(); + + if (triggers.includes("staffCreated")) { + return "This comment has been created by staff"; } + if (triggers.includes("created")) { + return "This comment has been created"; + } + if (triggers.includes("featured")) { + return "This comment has been featured"; + } + if (triggers.includes("pending")) { + return "This comment is pending"; + } + if (triggers.includes("reported")) { + return "This comment has been reported"; + } + + throw new Error("invalid trigger"); } public shouldPublishToChannel({ triggers }: GQLSlackChannel) { - const trigger = this.getTrigger(); + const triggerSet = this.getTriggers(); return ( - (triggers.allComments && trigger === "created") || - (triggers.featuredComments && trigger === "featured") || - (triggers.reportedComments && trigger === "reported") || - (triggers.pendingComments && trigger === "pending") || - (triggers.staffComments && trigger === "staffCreated") + (triggers.allComments && triggerSet.includes("created")) || + (triggers.featuredComments && triggerSet.includes("featured")) || + (triggers.reportedComments && triggerSet.includes("reported")) || + (triggers.pendingComments && triggerSet.includes("pending")) || + (triggers.staffComments && triggerSet.includes("staffCreated")) ); } - public getBlocks({ loaders, config, tenant, req }: GraphContext) { + public getContent({ loaders, config, tenant, req }: GraphContext) { const storyTitle = getStoryTitle(this.story); const moderateLink = reconstructTenantURL( config, @@ -94,32 +104,38 @@ export default class SlackPublishEvent { const commentLink = getURLWithCommentID(this.story.url, this.comment.id); const body = getHTMLPlainText(getLatestRevision(this.comment).body); - return [ - { - type: "section", - text: { - type: "mrkdwn", - text: `${this.getMessage()} on *<${this.story.url}|${storyTitle}>*`, - }, - }, - { type: "divider" }, - { - type: "section", - text: { - type: "plain_text", - text: body, - }, - }, - { - type: "context", - elements: [ - { + return { + text: body, + blocks: [ + { + type: "section", + block_id: "title-block", + text: { type: "mrkdwn", - text: `Authored by *${this.author.username}* | <${moderateLink}|Go to Moderation> | <${commentLink}|See Comment>`, + text: `${this.getMessage()} on *<${this.story.url}|${storyTitle}>*`, }, - ], - }, - { type: "divider" }, - ]; + }, + { type: "divider" }, + { + type: "section", + block_id: "body-block", + text: { + type: "plain_text", + text: body, + }, + }, + { + type: "context", + block_id: "footer-block", + elements: [ + { + type: "mrkdwn", + text: `Authored by *${this.author.username}* | <${moderateLink}|Go to Moderation> | <${commentLink}|See Comment>`, + }, + ], + }, + { type: "divider" }, + ], + }; } } diff --git a/src/core/server/events/listeners/slack/slack.ts b/src/core/server/events/listeners/slack/slack.ts index a29d419e4..9bfb7df9b 100644 --- a/src/core/server/events/listeners/slack/slack.ts +++ b/src/core/server/events/listeners/slack/slack.ts @@ -34,9 +34,9 @@ export class SlackCoralEventListener * postMessage will prepare and send the incoming Slack webhook. * * @param hookURL url to the Slack webhook that we should send the message to - * @param blocks the blocks for the message + * @param content the content for the message */ - private async postMessage(hookURL: string, blocks: any[]) { + private async postMessage(hookURL: string, content: any) { // Send the post to the Slack URL. We don't wrap this in a try/catch because // it's handled in the calling function. const res = await this.fetch(hookURL, { @@ -44,9 +44,7 @@ export class SlackCoralEventListener headers: { "Content-Type": "application/json", }, - body: JSON.stringify({ - blocks, - }), + body: JSON.stringify(content), }); // Check that the request was completed successfully. @@ -131,7 +129,7 @@ export class SlackCoralEventListener if (publishEvent.shouldPublishToChannel(channel)) { try { // Post the message to slack. - await this.postMessage(channel.hookURL, publishEvent.getBlocks(ctx)); + await this.postMessage(channel.hookURL, publishEvent.getContent(ctx)); } catch (err) { logger.error( { err, tenantID, payload, channel },