@@ -17,8 +17,7 @@ consistent between Slack and their Coder login.
17
17
Before setting up Slack notifications, ensure that you have the following:
18
18
19
19
- 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
22
21
23
22
## Create Slack Application
24
23
@@ -34,9 +33,9 @@ To integrate Slack with Coder, follow these steps to create a Slack application:
34
33
35
34
3 . Under "OAuth & Permissions", add the following OAuth scopes:
36
35
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.
40
39
41
40
4 . Install the app to your workspace and note down the ** Bot User OAuth Token**
42
41
from the "OAuth & Permissions" section.
@@ -52,128 +51,130 @@ To build the server to receive webhooks and interact with Slack:
52
51
53
52
1 . Initialize your project by running:
54
53
55
- ``` bash
56
- npm init -y
57
- ```
54
+ ``` bash
55
+ npm init -y
56
+ ```
58
57
59
58
2 . Install the Bolt library:
60
59
61
- ` ` ` bash
62
- npm install @slack/bolt
63
- ` ` `
60
+ ``` bash
61
+ npm install @slack/bolt
62
+ ```
64
63
65
64
3 . Create and edit the ` app.js ` file. Below is an example of the basic
66
65
structure:
67
66
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
+ ```
164
165
165
166
4 . Set environment variables to identify the Slack app:
166
167
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
+ ```
171
172
172
173
5 . Start the web application by running:
173
174
174
- ` ` ` bash
175
- node app.js
176
- ` ` `
175
+ ``` bash
176
+ node app.js
177
+ ```
177
178
178
179
## Enable Interactivity in Slack
179
180
@@ -192,11 +193,8 @@ must respond appropriately.
192
193
193
194
## Enable Webhook Integration in Coder
194
195
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:
200
198
201
199
``` bash
202
200
export CODER_NOTIFICATIONS_WEBHOOK_ENDPOINT=http://localhost:6000/v1/webhook`
0 commit comments