diff --git a/plugins/slack/README.md b/plugins/slack/README.md index 9074d85cf..e66b57feb 100644 --- a/plugins/slack/README.md +++ b/plugins/slack/README.md @@ -218,7 +218,7 @@ There are a lot of steps to creating a Slack app and installing it. Let's go over what you'll need to do to get set up with app auth. 1. [Create the app](https://api.slack.com/apps) -2. From your app's `Basic Information` page go to `Permissions => Bot Token Scopes` and add `chat:write` and `file:write` +2. From your app's `Basic Information` page go to `Permissions => Bot Token Scopes` and add `chat:write` and `file:write` (Optionally add `chat:write.customize` to use the `username` and `icon` options) 3. Copy the `Bot User OAuth Access Token` into your `.env` file and store it as `SLACK_TOKEN` 4. Install the app in the channels you want it to post to via the Slack UI diff --git a/plugins/slack/__tests__/__snapshots__/slack.test.ts.snap b/plugins/slack/__tests__/__snapshots__/slack.test.ts.snap index 9c9e115b0..24463166d 100644 --- a/plugins/slack/__tests__/__snapshots__/slack.test.ts.snap +++ b/plugins/slack/__tests__/__snapshots__/slack.test.ts.snap @@ -31,6 +31,13 @@ Array [ }, "type": "section", }, + Object { + "text": Object { + "text": " ", + "type": "mrkdwn", + }, + "type": "section", + }, Object { "type": "divider", }, @@ -49,6 +56,13 @@ Array [ }, "type": "section", }, + Object { + "text": Object { + "text": " ", + "type": "mrkdwn", + }, + "type": "section", + }, Object { "text": Object { "text": "*📝 Documentation*", @@ -63,6 +77,13 @@ Array [ }, "type": "section", }, + Object { + "text": Object { + "text": " ", + "type": "mrkdwn", + }, + "type": "section", + }, Object { "type": "divider", }, @@ -123,6 +144,13 @@ Array [ }, "type": "section", }, + Object { + "text": Object { + "text": " ", + "type": "mrkdwn", + }, + "type": "section", + }, Object { "type": "divider", }, @@ -141,6 +169,13 @@ Array [ }, "type": "section", }, + Object { + "text": Object { + "text": " ", + "type": "mrkdwn", + }, + "type": "section", + }, Object { "text": Object { "text": "*📝 Documentation*", @@ -155,6 +190,13 @@ Array [ }, "type": "section", }, + Object { + "text": Object { + "text": " ", + "type": "mrkdwn", + }, + "type": "section", + }, Object { "type": "divider", }, @@ -198,6 +240,13 @@ Array [ }, "type": "section", }, + Object { + "text": Object { + "text": " ", + "type": "mrkdwn", + }, + "type": "section", + }, Object { "type": "divider", }, @@ -258,8 +307,16 @@ Object { }, "type": "section", }, + Object { + "text": Object { + "text": " ", + "type": "mrkdwn", + }, + "type": "section", + }, ], "link_names": true, + "text": "New Releases: 1.0.0 :tada:", } `; @@ -298,8 +355,16 @@ Object { }, "type": "section", }, + Object { + "text": Object { + "text": " ", + "type": "mrkdwn", + }, + "type": "section", + }, ], "link_names": true, + "text": "New Releases: 1.0.0 :tada:", } `; @@ -344,8 +409,16 @@ Object { }, "type": "section", }, + Object { + "text": Object { + "text": " ", + "type": "mrkdwn", + }, + "type": "section", + }, ], "link_names": true, + "text": "New Release: :tada:", } `; @@ -383,8 +456,16 @@ Object { }, "type": "section", }, + Object { + "text": Object { + "text": " ", + "type": "mrkdwn", + }, + "type": "section", + }, ], "link_names": true, + "text": "New Release: :tada:", } `; @@ -422,8 +503,16 @@ Object { }, "type": "section", }, + Object { + "text": Object { + "text": " ", + "type": "mrkdwn", + }, + "type": "section", + }, ], "link_names": true, + "text": "New Release: :tada:", } `; @@ -461,8 +550,16 @@ Object { }, "type": "section", }, + Object { + "text": Object { + "text": " ", + "type": "mrkdwn", + }, + "type": "section", + }, ], "link_names": true, + "text": "New Releases: 1.0.0 :tada:", } `; @@ -500,8 +597,16 @@ Object { }, "type": "section", }, + Object { + "text": Object { + "text": " ", + "type": "mrkdwn", + }, + "type": "section", + }, ], "link_names": true, + "text": "New Releases: 1.0.0 :tada:", } `; @@ -539,8 +644,16 @@ Object { }, "type": "section", }, + Object { + "text": Object { + "text": " ", + "type": "mrkdwn", + }, + "type": "section", + }, ], "link_names": true, + "text": "New Release: :tada:", } `; @@ -578,8 +691,16 @@ Object { }, "type": "section", }, + Object { + "text": Object { + "text": " ", + "type": "mrkdwn", + }, + "type": "section", + }, ], "link_names": true, + "text": "New Releases: 1.0.0 :tada:", } `; @@ -631,5 +752,6 @@ Object { }, ], "link_names": true, + "text": "New Releases: 1.0.0 :tada:", } `; diff --git a/plugins/slack/src/index.ts b/plugins/slack/src/index.ts index 07b4dbed5..f9ae42241 100644 --- a/plugins/slack/src/index.ts +++ b/plugins/slack/src/index.ts @@ -48,6 +48,15 @@ const createSectionBlock = (text: string) => ({ }, }); +/** Create some space in the message */ +const createSpacerBlock = () => ({ + type: "section" as const, + text: { + type: "mrkdwn", + text: " ", + }, +}); + /** Create slack header block */ const createHeaderBlock = (text: string) => ({ type: "header" as const, @@ -96,6 +105,7 @@ export function convertToBlocks( if (line.startsWith("#")) { currentMessage.push(createSectionBlock(`*${line.replace(/^[#]+/, "")}*`)); } else if (line === "---") { + currentMessage.push(createSpacerBlock()); currentMessage.push(createDividerBlock()); } else if (line.startsWith("```")) { const [, language] = line.match(/```(\S+)/) || ["", "detect"]; @@ -147,6 +157,7 @@ export function convertToBlocks( } currentMessage.push(createSectionBlock(lines.join("\n"))); + currentMessage.push(createSpacerBlock()); } else if (line) { currentMessage.push(createSectionBlock(line)); } @@ -360,13 +371,14 @@ export default class SlackPlugin implements IPlugin { await last; if (Array.isArray(message)) { - await channels.reduce(async (lastMessage, channel) => { + await channels.reduce(async (lastMessage, channel, index) => { await lastMessage; await fetch("https://slack.com/api/chat.postMessage", { method: "POST", body: JSON.stringify({ ...userPostMessageOptions, channel, + text: index === 0 ? `${header} :tada:` : undefined, blocks: message, link_names: true, }), @@ -403,6 +415,7 @@ export default class SlackPlugin implements IPlugin { body: JSON.stringify({ ...userPostMessageOptions, link_names: true, + text: `${header} :tada:`, // If not in app auth only one message is constructed blocks: messages[0], }),