@slack/interactive-messages
Advanced tools
Comparing version 0.1.1 to 0.1.2
@@ -23,3 +23,3 @@ ### Description | ||
#### Reproducible in: | ||
slack-events version: | ||
@slack/interactive-messages version: | ||
node version: | ||
@@ -26,0 +26,0 @@ OS version(s): |
@@ -49,12 +49,17 @@ # Maintainers Guide | ||
1. Create the commit for the release: | ||
* Create and checkout a new branch for the release, for example `rel-v1.0.8`. | ||
* Bump the version number in adherence to [Semantic Versioning](http://semver.org/) in `package.json`. | ||
* Commit with a message including the new version number. For example `v1.0.8`. | ||
* Tag the commit with the version number. For example `v1.0.8`. | ||
* Push the branch to your fork (`git push username rel-v1.0.8`). | ||
* Open the PR from your fork to the origin repo. Get code review. Merge the PR. | ||
2. Distribute the release | ||
* Publish to the appropriate package manager. Once you have permission to publish on npm, you | ||
can run `npm publish`. | ||
* Create a GitHub Release. This will also serve as a Changelog for the project. Add a | ||
description of changes to the Changelog. Mention Issue and PR #'s and @-mention | ||
contributors. | ||
* In your local working copy, make sure you fetch from the repo, checkout and update your master branch. | ||
* Make sure the project has been built locally (`npm run build`). It is a good idea to reset the | ||
`/node_modules` directory and use a low LTS version of node (e.g. v4.x). | ||
* Publish to npm. Once you have permission to publish on npm, you can run `npm publish`. | ||
* Create a GitHub Release and the associated git tag. When creating a new release, make sure to | ||
select the commit with the version number as the commit messsage (e.g. `v1.0.8`). This will also | ||
serve as a Changelog for the project. Add a description of changes to the Changelog. Mention Issue | ||
and PR #'s and @-mention contributors. | ||
@@ -61,0 +66,0 @@ 3. (Slack Internal) Communicate the release internally. Include a link to the GitHub Release. |
{ | ||
"name": "@slack/interactive-messages", | ||
"version": "0.1.1", | ||
"description": "Slack interactive messages module", | ||
"version": "0.1.2", | ||
"description": "Slack Interactive Messages module", | ||
"main": "dist/index.js", | ||
@@ -29,2 +29,6 @@ "repository": "https://github.com/slackapi/node-slack-interactive-messages.git", | ||
}, | ||
"optionalDependencies": { | ||
"express": "^4.0.0", | ||
"body-parser": "^1.4.3" | ||
}, | ||
"devDependencies": { | ||
@@ -35,2 +39,3 @@ "babel-cli": "^6.24.0", | ||
"babel-preset-env": "^1.3.2", | ||
"body-parser": "^1.17.2", | ||
"codecov": "^2.1.0", | ||
@@ -43,2 +48,3 @@ "eslint": "^3.18.0", | ||
"eslint-plugin-react": "^6.10.3", | ||
"express": "^4.15.3", | ||
"get-random-port": "0.0.1", | ||
@@ -45,0 +51,0 @@ "mocha": "^3.2.0", |
103
README.md
# Interactive Message Adapter for Node and Express | ||
[![Build Status](https://travis-ci.org/slackapi/node-slack-interactive-messages.svg?branch=master)](https://travis-ci.org/slackapi/node-slack-interactive-messages) | ||
[![codecov](https://codecov.io/gh/slackapi/node-slack-interactive-messages/branch/master/graph/badge.svg)](https://codecov.io/gh/slackapi/node-slack-interactive-messages) | ||
The adapter provides a simplified API to route message actions and options in your app. It handles | ||
@@ -17,3 +20,3 @@ common tasks and best practices so that you don't need to. | ||
``` | ||
$ npm install --save @slack/interactive-messages express | ||
$ npm install --save @slack/interactive-messages express body-parser | ||
``` | ||
@@ -24,8 +27,12 @@ | ||
Before you can use [interactive messages](https://api.slack.com/interactive-messages) you must | ||
[create a Slack App](https://api.slack.com/apps/new), and configure an interactive message request | ||
URL. If your app will use dynamic menus, you also need to configure an options URL. | ||
[create a Slack App](https://api.slack.com/apps/new). On the **Basic Information** page, in the section | ||
for **App Credentials**, note the **Verification Token**. You will need it to initialize the adapter. | ||
Select the **Interactive Messages** feature, and enable it. Input your **Request URL**. If your app | ||
will use dynamic menus, you also need to input a **Options URL**. | ||
![Configuring a request URL](support/interactive-messages.gif) | ||
### Getting a temporary URL for development | ||
<details> | ||
<summary>Getting a temporary Request URL for development</summary> | ||
@@ -53,13 +60,17 @@ If you're just getting started with development, you may not have a publicly accessible URL for | ||
</details> | ||
## Usage | ||
The easiest way to start using interactive messages is by using the built-in HTTP server. | ||
The easiest way to start responding to interactive message actions is by using the built-in HTTP | ||
server. | ||
```javascript | ||
// Initialize using verification token from environment variables | ||
const createMessageAdapter = require('@slack/events-api').createMessageAdapter; | ||
const { createMessageAdapter } = require('@slack/interactive-messages'); | ||
// Initialize adapter using verification token from environment variables | ||
const slackMessages = createMessageAdapter(process.env.SLACK_VERIFICATION_TOKEN); | ||
const port = process.env.PORT || 3000; | ||
// Attach action handlers by `callback_id` (See: https://api.slack.com/docs/interactive-message-field-guide#attachment_fields) | ||
// Attach action handlers by `callback_id` | ||
// (See: https://api.slack.com/docs/interactive-message-field-guide#attachment_fields) | ||
slackMessages.action('welcome_button', (payload) => { | ||
@@ -69,7 +80,7 @@ // `payload` is JSON that describes an interaction with a message. | ||
// The `actions` array contains details about the particular action (button press, menu selection, etc.) | ||
// The `actions` array contains details about the specific action (button press, menu selection, etc.) | ||
const action = payload.actions[0]; | ||
console.log(`The button had name ${action.name} and value ${action.value}`); | ||
// You should return a value which describes a message to replace the original. | ||
// You should return a JSON object which describes a message to replace the original. | ||
// Note that the payload contains a copy of the original message (`payload.original_message`). | ||
@@ -83,3 +94,4 @@ const replacement = payload.original_message; | ||
// Start a basic HTTP server | ||
// Start the built-in HTTP server | ||
const port = process.env.PORT || 3000; | ||
slackMessages.start(port).then(() => { | ||
@@ -90,20 +102,21 @@ console.log(`server listening on port ${port}`); | ||
**NOTE**: To use the example above, your application must have already sent a message with an | ||
attachment that contains a button whose `callback_id` is set to `'welcome_button'`. There are | ||
multiple ways to produce these types of messages; including | ||
[incoming webhooks](https://api.slack.com/incoming-webhooks), or the web API (`chat.postMessage`, | ||
`chat.update`, or even `chat.unfurl`). | ||
**NOTE**: To use the example above, your application must have already sent a message with a message | ||
button whose `callback_id` is set to `'welcome_button'`. There are multiple ways to produce these | ||
types of messages; including [incoming webhooks](https://api.slack.com/incoming-webhooks), or the | ||
web API ([`chat.postMessage`](https://api.slack.com/methods/chat.postMessage), | ||
[`chat.update`](https://api.slack.com/methods/chat.update), or | ||
[`chat.unfurl`](https://api.slack.com/methods/chat.unfurl)). | ||
### Using with Express | ||
### Using as Express middleware | ||
For usage within an existing Express application, you can route requests to the adapter's express | ||
middleware by calling the `expressMiddleware()` method. | ||
For usage within an existing Express application, call the `expressMiddleware()` method on the | ||
adapter and it will return a middleware function which you can add to your app. Be sure to add | ||
the `body-parser` middleware before the message adapter as shown below. | ||
```javascript | ||
const http = require('http'); | ||
const { createMessageAdapter } = require('@slack/interactive-messages'); | ||
// Initialize using verification token from environment variables | ||
const createMessageAdapter = require('@slack/events-api').createMessageAdapter; | ||
const slackMessages = createMessageAdapter(process.env.SLACK_VERIFICATION_TOKEN); | ||
const port = process.env.PORT || 3000; | ||
@@ -119,3 +132,3 @@ // Initialize an Express application | ||
// Mount the event handler on a route | ||
// NOTE: you must mount to a path that matches the Action URL or Options URL that was configured | ||
// NOTE: you must mount to a path that matches the Request URL and/or Options URL that was configured | ||
app.use('/slack/actions', slackMessages.expressMiddleware()); | ||
@@ -129,2 +142,3 @@ | ||
// Start the express application | ||
const port = process.env.PORT || 3000; | ||
http.createServer(app).listen(port, () => { | ||
@@ -135,3 +149,3 @@ console.log(`server listening on port ${port}`); | ||
**Pro-Tip**: You can use this technique to combine usage of this module with the | ||
**Pro-Tip**: You can use this technique to combine usage of this adapter and the | ||
[Events API adapter](https://github.com/slackapi/node-slack-events-api). | ||
@@ -141,3 +155,3 @@ | ||
You can attach handlers to actions using more than just exact matches for the `"callback_id"` string. | ||
You can attach handlers for actions using more than just exact matches for the `"callback_id"` string. | ||
@@ -151,3 +165,4 @@ ```javascript | ||
// Handle actions from unfurls separately, since properties in the payload can differ (e.g. no access to original_message content) | ||
// Handle actions from unfurls separately, since properties in the payload can differ | ||
// (e.g. no access to original_message content) | ||
slackMessages.action({ unfurl: true }, (payload) => { /* ... */ }); | ||
@@ -160,12 +175,11 @@ | ||
These options should allow you to match actions in any way that makes sense for your app. Keep in | ||
mind that only the first handler to be registered that matches an incoming interaction will be | ||
invoked. | ||
mind that only the first handler that matches an action will be invoked. | ||
### Asynchronous responses | ||
You cannot always prepare a response to an action synchronously, especially in node where all I/O is | ||
evented and you should not block. In these situations we recommend that you at least consider | ||
updating the message to remove the interactive elements when the action shouldn't trigger more than | ||
once. The adapter will invoke your handler with a `respond()` function as the second argument so you | ||
can update the message again asynchronously. | ||
Sometimes you can't prepare a response to an action synchronously, especially in node where all I/O | ||
is evented and you should not block. In these situations we recommend that you at least consider | ||
updating the message to remove the interactive elements (often times actions aren't meant to trigger | ||
more than once for an app). The adapter will invoke your handler with a `respond()` function as the | ||
second argument so you can update the message asynchronously. | ||
@@ -191,2 +205,6 @@ ```javascript | ||
If you do not return a value or return a falsy value from the handler, the message is not replaced. | ||
This is not recommended in most cases, because the interactive attachments will stay on the message | ||
and might be used again by the same or a different user. | ||
### Options handling and matching | ||
@@ -197,3 +215,3 @@ | ||
If you are using the built-in HTTP server, the URL for options requests will be the same as the | ||
action URL. If you are using the adapter with Express, the options URL can be anywhere you mount | ||
request URL. If you are using the adapter with Express, the options URL can be anywhere you mount | ||
the express middleware. | ||
@@ -206,4 +224,5 @@ | ||
// `app` is an express application | ||
// this is an example of choosing to use a separate action URL and options URL | ||
app.use('/slack/actions', messageMiddleware) | ||
// in this example, we create a separate Options URL (different from the above Request URL) and | ||
// still use the same instance of the adapter's middleware | ||
app.use('/slack/options', messageMiddleware); | ||
@@ -232,3 +251,3 @@ | ||
.action('make_order_3', orderStepThree) | ||
.options('make_order_3', workFlowStepThreeOptions); | ||
.options('make_order_3', orderStepThreeOptions); | ||
``` | ||
@@ -245,9 +264,5 @@ | ||
## Documentation | ||
To learn more, see the [reference documentation](docs/reference.md). | ||
## Support | ||
Need help? Join the [Bot Developer Hangout](http://dev4slack.xoxco.com/) team and talk to us in | ||
Need help? Join the [Bot Developer Hangout](https://community.botkit.ai/) team and talk to us in | ||
[#slack-api](https://dev4slack.slack.com/messages/slack-api/). | ||
@@ -257,7 +272,1 @@ | ||
right here on GitHub. | ||
**TODO** mention falsy returns do no replacement in reference docs | ||
**TODO** open an issue to discuss adding API for inspection or removal of action/option handlers | ||
**TODO** open an issue to discuss if we need options for name or value matching/routing? slapp has these |
@@ -177,2 +177,3 @@ import http from 'http'; | ||
const action = payload.actions && payload.actions[0]; | ||
// The following result value represents "no replacement" | ||
let result = { status: 200 }; | ||
@@ -194,3 +195,3 @@ const respond = (message) => { | ||
} | ||
if (isRegExp(constraints.callbackId) && !constraints.callbackId.text(payload.callback_id)) { | ||
if (isRegExp(constraints.callbackId) && !constraints.callbackId.test(payload.callback_id)) { | ||
return false; | ||
@@ -197,0 +198,0 @@ } |
@@ -6,2 +6,3 @@ var assert = require('assert'); | ||
var getRandomPort = require('get-random-port'); | ||
var isFunction = require('lodash.isfunction'); | ||
var systemUnderTest = require('../../dist/adapter'); | ||
@@ -93,3 +94,4 @@ var SlackMessageAdapter = systemUnderTest.default; | ||
return this.adapter.start(self.portNumber).then(function (server) { | ||
assert(server.listening); | ||
// only works in node >= 5.7.0 | ||
// assert(server.listening); | ||
assert.equal(server.address().port, self.portNumber); | ||
@@ -99,2 +101,48 @@ }); | ||
}); | ||
describe('#stop()', function () { | ||
beforeEach(function (done) { | ||
var self = this; | ||
self.adapter = new SlackMessageAdapter(workingVerificationToken); | ||
getRandomPort(function (error, port) { | ||
if (error) return done(error); | ||
return self.adapter.start(port) | ||
.then(function (server) { | ||
self.server = server; | ||
done(); | ||
}) | ||
.catch(done); | ||
}); | ||
}); | ||
afterEach(function () { | ||
return this.adapter.stop().catch(nop); | ||
}); | ||
it('should return a Promise and the server should be stopped', function () { | ||
var self = this; | ||
return this.adapter.stop().then(function () { | ||
assert(!self.server.listening); | ||
}); | ||
}); | ||
}); | ||
describe('#expressMiddleware()', function () { | ||
beforeEach(function () { | ||
this.adapter = new SlackMessageAdapter(workingVerificationToken); | ||
}); | ||
it('should return a function', function () { | ||
var middleware = this.adapter.expressMiddleware(); | ||
assert(isFunction(middleware)); | ||
}); | ||
}); | ||
describe('#action()', function () { | ||
beforeEach(function () { | ||
this.adapter = new SlackMessageAdapter(workingVerificationToken); | ||
}); | ||
it('should fail registration without handler', function () { | ||
assert.throws(function () { | ||
this.adapter.action('my_callback'); | ||
}, TypeError); | ||
}); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1820593
39
1312
255
8
18
5