@@ -17,8 +17,7 @@ consistent between Slack and their Coder login.
1717Before setting up Slack notifications, ensure that you have the following:
1818
1919- Administrator access to the Slack platform to create apps
20- - Coder platform v2.15.0 or greater with
21- [ notifications enabled] ( ./index.md#enable-experiment ) for versions <v2.16.0
20+ - Coder platform >=v2.16.0
2221
2322## Create Slack Application
2423
@@ -34,9 +33,9 @@ To integrate Slack with Coder, follow these steps to create a Slack application:
3433
35343 . Under "OAuth & Permissions", add the following OAuth scopes:
3635
37- - ` chat:write ` : To send messages as the app.
38- - ` users:read ` : To find the user details.
39- - ` users:read.email ` : To find user emails.
36+ - ` chat:write ` : To send messages as the app.
37+ - ` users:read ` : To find the user details.
38+ - ` users:read.email ` : To find user emails.
4039
41404 . Install the app to your workspace and note down the ** Bot User OAuth Token**
4241 from the "OAuth & Permissions" section.
@@ -52,128 +51,130 @@ To build the server to receive webhooks and interact with Slack:
5251
53521 . Initialize your project by running:
5453
55- ``` bash
56- npm init -y
57- ```
54+ ``` bash
55+ npm init -y
56+ ```
5857
59582 . Install the Bolt library:
6059
61- ` ` ` bash
62- npm install @slack/bolt
63- ` ` `
60+ ``` bash
61+ npm install @slack/bolt
62+ ```
6463
65643 . Create and edit the ` app.js ` file. Below is an example of the basic
6665 structure:
6766
68- ` ` ` js
69- const { App, LogLevel, ExpressReceiver } = require(" @slack/bolt" );
70- const bodyParser = require(" body-parser" );
71-
72- const port = process.env.PORT || 6000;
73-
74- // Create a Bolt Receiver
75- const receiver = new ExpressReceiver({
76- signingSecret: process.env.SLACK_SIGNING_SECRET,
77- });
78- receiver.router.use(bodyParser.json ());
79-
80- // Create the Bolt App, using the receiver
81- const app = new App({
82- token: process.env.SLACK_BOT_TOKEN,
83- logLevel: LogLevel.DEBUG,
84- receiver,
85- });
86-
87- receiver.router.post(" /v1/webhook" , async (req, res) => {
88- try {
89- if (! req.body) {
90- return res.status(400).send(" Error: request body is missing" );
91- }
92-
93- const { title, body } = req.body;
94- if (! title || ! body) {
95- return res.status(400).send(' Error: missing fields: "title", or "body"' );
96- }
97-
98- const payload = req.body.payload;
99- if (! payload) {
100- return res.status(400).send(' Error: missing "payload" field' );
101- }
102-
103- const { user_email, actions } = payload;
104- if (! user_email || ! actions) {
105- return res
106- .status(400)
107- .send(' Error: missing fields: "user_email", "actions"' );
108- }
109-
110- // Get the user ID using Slack API
111- const userByEmail = await app.client.users.lookupByEmail({
112- email: user_email,
113- });
114-
115- const slackMessage = {
116- channel: userByEmail.user.id,
117- text: body,
118- blocks: [
119- {
120- type: " header" ,
121- text: { type: " plain_text" , text: title },
122- },
123- {
124- type: " section" ,
125- text: { type: " mrkdwn" , text: body },
126- },
127- ],
128- };
129-
130- // Add action buttons if they exist
131- if (actions && actions.length > 0) {
132- slackMessage.blocks.push({
133- type: " actions" ,
134- elements: actions.map(( action) => ({
135- type: "button",
136- text: { type: "plain_text", text: action.label },
137- url: action.url,
138- })) ,
139- });
140- }
141-
142- // Post message to the user on Slack
143- await app.client.chat.postMessage(slackMessage);
144-
145- res.status(204).send ();
146- } catch (error) {
147- console.error(" Error sending message:" , error);
148- res.status(500).send ();
149- }
150- });
151-
152- // Acknowledge clicks on link_button, otherwise Slack UI
153- // complains about missing events.
154- app.action(" button_click" , async ({ body, ack, say }) => {
155- await ack (); // no specific action needed
156- });
157-
158- // Start the Bolt app
159- (async () => {
160- await app.start(port);
161- console.log(" ⚡️ Coder Slack bot is running!" );
162- })();
163- ` ` `
67+ ``` js
68+ const { App , LogLevel , ExpressReceiver } = require (" @slack/bolt" );
69+ const bodyParser = require (" body-parser" );
70+
71+ const port = process .env .PORT || 6000 ;
72+
73+ // Create a Bolt Receiver
74+ const receiver = new ExpressReceiver ({
75+ signingSecret: process .env .SLACK_SIGNING_SECRET ,
76+ });
77+ receiver .router .use (bodyParser .json ());
78+
79+ // Create the Bolt App, using the receiver
80+ const app = new App ({
81+ token: process .env .SLACK_BOT_TOKEN ,
82+ logLevel: LogLevel .DEBUG ,
83+ receiver,
84+ });
85+
86+ receiver .router .post (" /v1/webhook" , async (req , res ) => {
87+ try {
88+ if (! req .body ) {
89+ return res .status (400 ).send (" Error: request body is missing" );
90+ }
91+
92+ const { title , body } = req .body ;
93+ if (! title || ! body) {
94+ return res
95+ .status (400 )
96+ .send (' Error: missing fields: "title", or "body"' );
97+ }
98+
99+ const payload = req .body .payload ;
100+ if (! payload) {
101+ return res .status (400 ).send (' Error: missing "payload" field' );
102+ }
103+
104+ const { user_email , actions } = payload;
105+ if (! user_email || ! actions) {
106+ return res
107+ .status (400 )
108+ .send (' Error: missing fields: "user_email", "actions"' );
109+ }
110+
111+ // Get the user ID using Slack API
112+ const userByEmail = await app .client .users .lookupByEmail ({
113+ email: user_email,
114+ });
115+
116+ const slackMessage = {
117+ channel: userByEmail .user .id ,
118+ text: body,
119+ blocks: [
120+ {
121+ type: " header" ,
122+ text: { type: " plain_text" , text: title },
123+ },
124+ {
125+ type: " section" ,
126+ text: { type: " mrkdwn" , text: body },
127+ },
128+ ],
129+ };
130+
131+ // Add action buttons if they exist
132+ if (actions && actions .length > 0 ) {
133+ slackMessage .blocks .push ({
134+ type: " actions" ,
135+ elements: actions .map ((action ) => ({
136+ type: " button" ,
137+ text: { type: " plain_text" , text: action .label },
138+ url: action .url ,
139+ })),
140+ });
141+ }
142+
143+ // Post message to the user on Slack
144+ await app .client .chat .postMessage (slackMessage);
145+
146+ res .status (204 ).send ();
147+ } catch (error) {
148+ console .error (" Error sending message:" , error);
149+ res .status (500 ).send ();
150+ }
151+ });
152+
153+ // Acknowledge clicks on link_button, otherwise Slack UI
154+ // complains about missing events.
155+ app .action (" button_click" , async ({ body, ack, say }) => {
156+ await ack (); // no specific action needed
157+ });
158+
159+ // Start the Bolt app
160+ (async () => {
161+ await app .start (port);
162+ console .log (" ⚡️ Coder Slack bot is running!" );
163+ })();
164+ ```
164165
1651664 . Set environment variables to identify the Slack app:
166167
167- ` ` ` bash
168- export SLACK_BOT_TOKEN=xoxb-...
169- export SLACK_SIGNING_SECRET=0da4b...
170- ` ` `
168+ ``` bash
169+ export SLACK_BOT_TOKEN=xoxb-...
170+ export SLACK_SIGNING_SECRET=0da4b...
171+ ```
171172
1721735 . Start the web application by running:
173174
174- ` ` ` bash
175- node app.js
176- ` ` `
175+ ``` bash
176+ node app.js
177+ ```
177178
178179## Enable Interactivity in Slack
179180
@@ -192,11 +193,8 @@ must respond appropriately.
192193
193194## Enable Webhook Integration in Coder
194195
195- To enable webhook integration in Coder, ensure the "notifications"
196- [experiment is activated](./index.md#enable-experiment) (only required in
197- v2.15.X).
198-
199- Then, define the POST webhook endpoint matching the deployed Slack bot:
196+ To enable webhook integration in Coder, define the POST webhook endpoint
197+ matching the deployed Slack bot:
200198
201199``` bash
202200export CODER_NOTIFICATIONS_WEBHOOK_ENDPOINT=http://localhost:6000/v1/webhook`
0 commit comments