Skip to content

Commit

Permalink
Merge pull request #6 from slackapi/granular_bot_permissions
Browse files Browse the repository at this point in the history
Granular bot permissions
  • Loading branch information
girliemac authored Jan 24, 2020
2 parents 2b01bb0 + 6865598 commit c8d7883
Show file tree
Hide file tree
Showing 7 changed files with 199 additions and 204 deletions.
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ Use a slash command and a dialog to create a helpdesk ticket in a 3rd-party syst

1. Create an app at [https://api.slack.com/apps](https://api.slack.com/apps)
2. Add a Slash command (See *Add a Slash Command* section below)
3. Navigate to **Bot Users** and click "Add a Bot User" to create one.
4. Enable Interactive components (See *Enable Interactive Components* below)
5. Navigate to the **OAuth & Permissions** page and make sure the following scopes are pre-selected:
3. Enable Interactive components (See *Enable Interactive Components* below)
4. Navigate to the **OAuth & Permissions** page and select the following bot token scopes:
* `commands`
* `bot`
6. Click 'Save Changes' and install the app (You should get an OAuth access token after the installation)
* `chat:write`
* `users:read`
* `users:read.email`
* `im:write`
5. Click 'Save Changes' and install the app (You should get an OAuth access token after the installation)

#### Add a Slash Command
1. Go back to the app settings and click on Slash Commands.
Expand Down
13 changes: 13 additions & 0 deletions src/api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const axios = require('axios');
const qs = require('querystring');
const apiUrl = 'https://slack.com/api';

const callAPIMethod = async (method, payload) => {
let data = Object.assign({ token: process.env.SLACK_ACCESS_TOKEN }, payload);
let result = await axios.post(`${apiUrl}/${method}`, qs.stringify(data));
return result.data;
}

module.exports = {
callAPIMethod
}
153 changes: 28 additions & 125 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
require('dotenv').config();

const axios = require('axios');
const express = require('express');
const bodyParser = require('body-parser');
const qs = require('querystring');
const ticket = require('./ticket');
const signature = require('./verifySignature');
const api = require('./api');
const payloads = require('./payloads');
const debug = require('debug')('slash-command-template:index');

const apiUrl = 'https://slack.com/api';

const app = express();

/*
Expand All @@ -24,149 +22,54 @@ const rawBodyBuffer = (req, res, buf, encoding) => {
}
};

app.use(bodyParser.urlencoded({verify: rawBodyBuffer, extended: true }));
app.use(bodyParser.urlencoded({ verify: rawBodyBuffer, extended: true }));
app.use(bodyParser.json({ verify: rawBodyBuffer }));

app.get('/', (req, res) => {
res.send('<h2>The Slash Command and Dialog app is running</h2> <p>Follow the' +
' instructions in the README to configure the Slack App and your environment variables.</p>');
' instructions in the README to configure the Slack App and your environment variables.</p>');
});

/*
* Endpoint to receive /helpdesk slash command from Slack.
* Checks verification token and opens a dialog to capture more info.
*/
app.post('/command', (req, res) => {
app.post('/command', async (req, res) => {
// Verify the signing secret
if (!signature.isVerified(req)) {
debug('Verification token mismatch');
return res.status(404).send();
}

// extract the slash command text, and trigger ID from payload
const { text, trigger_id } = req.body;
const { trigger_id } = req.body;

// Verify the signing secret
if (signature.isVerified(req)) {
// create the dialog payload - includes the dialog structure, Slack API token,
// and trigger ID
const view = {
token: process.env.SLACK_ACCESS_TOKEN,
trigger_id,
view: JSON.stringify({
type: 'modal',
title: {
type: 'plain_text',
text: 'Submit a helpdesk ticket'
},
callback_id: 'submit-ticket',
submit: {
type: 'plain_text',
text: 'Submit'
},
blocks: [
{
block_id: 'title_block',
type: 'input',
label: {
type: 'plain_text',
text: 'Title'
},
element: {
action_id: 'title',
type: 'plain_text_input'
},
hint: {
type: 'plain_text',
text: '30 second summary of the problem'
}
},
{
block_id: 'description_block',
type: 'input',
label: {
type: 'plain_text',
text: 'Description'
},
element: {
action_id: 'description',
type: 'plain_text_input',
multiline: true
},
optional: true
},
{
block_id: 'urgency_block',
type: 'input',
label: {
type: 'plain_text',
text: 'Importance'
},
element: {
action_id: 'urgency',
type: 'static_select',
options: [
{
text: {
type: "plain_text",
text: "High"
},
value: "high"
},
{
text: {
type: "plain_text",
text: "Medium"
},
value: "medium"
},
{
text: {
type: "plain_text",
text: "Low"
},
value: "low"
}
]
},
optional: true
}
]
})
};
// create the modal payload - includes the dialog structure, Slack API token,
// and trigger ID
let view = payloads.modal({
trigger_id
});

console.log('open view')
let result = await api.callAPIMethod('views.open', view);

// open the dialog by calling dialogs.open method and sending the payload
axios.post(`${apiUrl}/views.open`, qs.stringify(view))
.then((result) => {
debug('views.open: %o', result.data);
res.send('');
}).catch((err) => {
debug('views.open call failed: %o', err);
res.sendStatus(500);
});
} else {
debug('Verification token mismatch');
res.sendStatus(404);
}
debug('views.open: %o', result);
return res.send('');
});

/*
* Endpoint to receive the dialog submission. Checks the verification token
* and creates a Helpdesk ticket
*/
app.post('/interactive', (req, res) => {
const body = JSON.parse(req.body.payload);

// check that the verification token matches expected value
if (signature.isVerified(req)) {
debug(`Form submission received: ${body.view}`);

// immediately respond with a empty 200 response to let
// Slack know the command was received
res.send('');

// create Helpdesk ticket
ticket.create(body.user.id, body.view);
} else {
debug('Token mismatch');
res.sendStatus(404);
// Verify the signing secret
if (!signature.isVerified(req)) {
debug('Verification token mismatch');
return res.status(404).send();
}

const body = JSON.parse(req.body.payload);
res.send('');
ticket.create(body.user.id, body.view);
});

const server = app.listen(process.env.PORT || 5000, () => {
Expand Down
121 changes: 121 additions & 0 deletions src/payloads.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
module.exports = {
confirmation: context => {
return {
channel: context.channel_id,
text: 'Helpdesk ticket created!',
blocks: JSON.stringify([
{
type: 'section',
text: {
type: 'mrkdwn',
text: '*Helpdesk ticket created!*'
}
},
{
type: 'divider'
},
{
type: 'section',
text: {
type: 'mrkdwn',
text: `*Title*\n${context.title}\n\n*Description*\n${context.description}`
}
},
{
type: 'context',
elements: [
{
type: 'mrkdwn',
text: `*Urgency*: ${context.urgency}`
}
]
}
])
}
},
modal: context => {
return {
trigger_id: context.trigger_id,
view: JSON.stringify({
type: 'modal',
title: {
type: 'plain_text',
text: 'Submit a helpdesk ticket'
},
callback_id: 'submit-ticket',
submit: {
type: 'plain_text',
text: 'Submit'
},
blocks: [
{
block_id: 'title_block',
type: 'input',
label: {
type: 'plain_text',
text: 'Title'
},
element: {
action_id: 'title',
type: 'plain_text_input'
},
hint: {
type: 'plain_text',
text: '30 second summary of the problem'
}
},
{
block_id: 'description_block',
type: 'input',
label: {
type: 'plain_text',
text: 'Description'
},
element: {
action_id: 'description',
type: 'plain_text_input',
multiline: true
},
optional: true
},
{
block_id: 'urgency_block',
type: 'input',
label: {
type: 'plain_text',
text: 'Importance'
},
element: {
action_id: 'urgency',
type: 'static_select',
options: [
{
text: {
type: "plain_text",
text: "High"
},
value: "high"
},
{
text: {
type: "plain_text",
text: "Medium"
},
value: "medium"
},
{
text: {
type: "plain_text",
text: "Low"
},
value: "low"
}
]
},
optional: true
}
]
})
}
}
}
Loading

0 comments on commit c8d7883

Please sign in to comment.