diff --git a/README.md b/README.md index ac876dbc9..5e1fea915 100644 --- a/README.md +++ b/README.md @@ -390,6 +390,10 @@ slapp.route('handleDoitConfirmation', (msg, state) => { - [Slapp.use()](#slappusefnfunction) - [Slapp.attachToExpress()](#slappattachtoexpressappobjectoptsobject) + - [Slapp.receiveEvent()](#slappreceiveeventeventobject) + - [Slapp.receiveCommand()](#slappreceivecommandcommandobject) + - [Slapp.receiveAction()](#slappreceiveactionactionobject) + - [Slapp.receiveOptions()](#slappreceiveoptionsoptionobject) - [Slapp.route()](#slapproutefnkeystringfnfunction) - [Slapp.getRoute()](#slappgetroutefnkeystring) - [Slapp.match()](#slappmatchfnfunction) @@ -454,6 +458,34 @@ slapp.route('handleDoitConfirmation', (msg, state) => { }) ``` +## Slapp.receiveEvent(event:Object) + + Receive a new event from slack, this is useful if you are not using express to handle events from slack + +#### Parameters + - `event` string - JSON event payload from slack + +## Slapp.receiveCommand(command:Object) + + Receive a new slash command from slack, this is useful if you are not using express to handle commands from slack + +#### Parameters + - `command` string - JSON slash command payload from slack + +## Slapp.receiveAction(action:Object) + + Receive a new interactive button action from slack, this is useful if you are not using express to handle actions from slack + +#### Parameters + - `action` string - JSON button action payload from slack + +## Slapp.receiveOptions(option:Object) + + Receive a new interactive menu load from slack, this is useful if you are not using express to handle options from slack + +#### Parameters + - `option` string - JSON option payload from slack + ## Slapp.route(fnKey:string, fn:function) Register a new function route diff --git a/src/receiver/index.js b/src/receiver/index.js index ada7ce9a2..53f8f29e3 100644 --- a/src/receiver/index.js +++ b/src/receiver/index.js @@ -119,5 +119,35 @@ module.exports = class Receiver extends EventEmitter { msg.attachResponse(response, timeout) } + receiveHandler (slappData, callback) { + let req = { slapp: slappData } + + this.context(req, null, err => { + if (err) { + return callback(err) + } + + let message = req.slapp + let msg = new Message(message.type, message.body, message.meta) + + this.emit('message', msg) + }) + } + + receiveEvent (event, callback) { + this.receiveHandler(ParseEvent.slappData(event), callback) + } + + receiveCommand (command, callback) { + this.receiveHandler(ParseCommand.slappData(command), callback) + } + + receiveAction (action, callback) { + this.receiveHandler(ParseAction.slappData(action), callback) + } + + receiveOptions (option, callback) { + this.receiveHandler(ParseOptions.slappData(option), callback) + } } diff --git a/src/receiver/middleware/parse-action.js b/src/receiver/middleware/parse-action.js index 12c57dbce..d3870a0c5 100644 --- a/src/receiver/middleware/parse-action.js +++ b/src/receiver/middleware/parse-action.js @@ -2,7 +2,9 @@ const bodyParser = require('body-parser') -module.exports = () => { +let parse + +module.exports = parse = () => { return [ bodyParser.urlencoded({extended: true}), bodyParser.text({type: '*/*'}), @@ -19,21 +21,26 @@ module.exports = () => { return res.send('Error parsing payload') } - req.slapp = { - type: 'action', - body: body, - meta: { - verify_token: body.token, - user_id: body.user.id, - channel_id: body.channel.id, - team_id: body.team.id - }, - // Message actions may be responded to directly within 3000ms - response: res, - responseTimeout: 2500 - } + req.slapp = parse.slappData(body) + + // Message actions may be responded to directly within 3000ms + req.slapp.response = res + req.slapp.responseTimeout = 2500 next() } ] } + +parse.slappData = body => { + return { + type: 'action', + body: body, + meta: { + verify_token: body.token, + user_id: body.user && body.user.id, + channel_id: body.channel && body.channel.id, + team_id: body.team && body.team.id + } + } +} diff --git a/src/receiver/middleware/parse-command.js b/src/receiver/middleware/parse-command.js index fd1f2a92e..4380bb66c 100644 --- a/src/receiver/middleware/parse-command.js +++ b/src/receiver/middleware/parse-command.js @@ -2,27 +2,34 @@ const bodyParser = require('body-parser') -module.exports = () => { +let parse + +module.exports = parse = () => { return [ bodyParser.urlencoded({extended: true}), function parseCommand (req, res, next) { let body = req.body - req.slapp = { - type: 'command', - body: body, - meta: { - verify_token: body.token, - user_id: body.user_id, - channel_id: body.channel_id, - team_id: body.team_id - }, - // Slash Commands requests may be responded to directly within 3000ms - response: res, - responseTimeout: 2500 - } + req.slapp = parse.slappData(body) + + // Message actions may be responded to directly within 3000ms + req.slapp.response = res + req.slapp.responseTimeout = 2500 next() } ] } + +parse.slappData = body => { + return { + type: 'command', + body: body, + meta: { + verify_token: body.token, + user_id: body.user_id, + channel_id: body.channel_id, + team_id: body.team_id + } + } +} diff --git a/src/receiver/middleware/parse-event.js b/src/receiver/middleware/parse-event.js index 883ff7479..0aaffc102 100644 --- a/src/receiver/middleware/parse-event.js +++ b/src/receiver/middleware/parse-event.js @@ -2,7 +2,9 @@ const bodyParser = require('body-parser') -module.exports = () => { +let parse + +module.exports = parse = () => { return [ bodyParser.json(), function handleChallenge (req, res, next) { @@ -18,22 +20,27 @@ module.exports = () => { }, function parseEvent (req, res, next) { let body = req.body || {} - let event = body.event || {} - let channelId = event.channel || (event.item && event.item.channel) - - req.slapp = { - type: 'event', - body: body, - meta: { - verify_token: body.token, - user_id: event.user, - bot_id: event.bot_id, - channel_id: channelId, - team_id: body.team_id - } - } + + req.slapp = parse.slappData(body) next() } ] } + +parse.slappData = (body) => { + let event = body.event || {} + let channelId = event.channel || (event.item && event.item.channel) + + return { + type: 'event', + body: body, + meta: { + verify_token: body.token, + user_id: event.user, + bot_id: event.bot_id, + channel_id: channelId, + team_id: body.team_id + } + } +} diff --git a/src/receiver/middleware/parse-options.js b/src/receiver/middleware/parse-options.js index 0f595d720..3db8791b5 100644 --- a/src/receiver/middleware/parse-options.js +++ b/src/receiver/middleware/parse-options.js @@ -2,7 +2,9 @@ const bodyParser = require('body-parser') -module.exports = () => { +let parse + +module.exports = parse = () => { return [ bodyParser.urlencoded({extended: true}), function parseOptions (req, res, next) { @@ -18,21 +20,26 @@ module.exports = () => { return next(new Error('Error parsing payload')) } - req.slapp = { - type: 'options', - body: body, - meta: { - verify_token: body.token, - user_id: body.user && body.user.id, - channel_id: body.channel && body.channel.id, - team_id: body.team && body.team.id - }, - // Options must be handled very quickly within ??? - response: res, - responseTimeout: 3000 - } + req.slapp = parse.slappData(body) + + // Message actions may be responded to directly within 3000ms + req.slapp.response = res + req.slapp.responseTimeout = 3000 next() } ] } + +parse.slappData = body => { + return { + type: 'options', + body: body, + meta: { + verify_token: body.token, + user_id: body.user && body.user.id, + channel_id: body.channel && body.channel.id, + team_id: body.team && body.team.id + } + } +} diff --git a/src/slapp.js b/src/slapp.js index aa11f4ce0..93d2e1503 100644 --- a/src/slapp.js +++ b/src/slapp.js @@ -298,6 +298,58 @@ class Slapp extends EventEmitter { return this.receiver.attachToExpress(app, opts) } + /** + * Receive a new event from slack, this is useful if you are not using express to handle events from slack + * + * ##### Parameters + * - `event` string - JSON event payload from slack + * + * @param {Object} event - event payload from slack + */ + + receiveEvent (event) { + return this.receiver.receiveEvent(event) + } + + /** + * Receive a new slash command from slack, this is useful if you are not using express to handle commands from slack + * + * ##### Parameters + * - `command` string - JSON slash command payload from slack + * + * @param {Object} command - slash command payload from slack + */ + + receiveCommand (command) { + return this.receiver.receiveCommand(command) + } + + /** + * Receive a new interactive button action from slack, this is useful if you are not using express to handle actions from slack + * + * ##### Parameters + * - `action` string - JSON button action payload from slack + * + * @param {Object} action - button action payload from slack + */ + + receiveAction (action) { + return this.receiver.receiveAction(action) + } + + /** + * Receive a new interactive menu load from slack, this is useful if you are not using express to handle options from slack + * + * ##### Parameters + * - `option` string - JSON option payload from slack + * + * @param {Object} option - option payload from slack + */ + + receiveOptions (option) { + return this.receiver.receiveOptions(option) + } + /** * Register a new function route * diff --git a/test/receiver.test.js b/test/receiver.test.js index 1b649ae3f..9fd1ead51 100644 --- a/test/receiver.test.js +++ b/test/receiver.test.js @@ -75,7 +75,7 @@ test('Receiver.emitHandler() w/ debug', t => { let receiver = new Receiver({ debug: true }) - let msg = getMockMessage() + let msg = getMockEventMessage() let res = fixtures.getMockRes() let emitStub = sinon.stub(receiver, 'emit') @@ -89,7 +89,7 @@ test('Receiver.emitHandler() w/ debug', t => { test('Receiver.emitHandler() w/o debug', t => { let receiver = new Receiver() - let msg = getMockMessage() + let msg = getMockEventMessage() let res = fixtures.getMockRes() let emitStub = sinon.stub(receiver, 'emit') @@ -103,7 +103,7 @@ test('Receiver.emitHandler() w/o debug', t => { test('Receiver.emitHandler() attachResponse', t => { let receiver = new Receiver() - let msg = getMockMessage() + let msg = getMockEventMessage() let res = fixtures.getMockRes() msg.response = res @@ -129,11 +129,71 @@ test('Receiver.attachResponse()', t => { t.true(attachStub.calledOnce) }) -function getMockMessage () { +test('Receiver.receiveEvent()', t => { + let receiver = new Receiver() + let msg = getMockEventMessage() + let emitStub = sinon.stub(receiver, 'emit') + + receiver.receiveEvent(msg.body) + + const emitted = emitStub.firstCall.args[1] + + t.true(emitStub.calledOnce) + t.is(emitted.type, 'event') + t.deepEqual(emitted.body, msg.body) + t.deepEqual(emitted.meta, msg.meta) +}) + +test('Receiver.receiveCommand()', t => { + let receiver = new Receiver() + let msg = getMockCommandMessage() + let emitStub = sinon.stub(receiver, 'emit') + + receiver.receiveCommand(msg.body) + + const emitted = emitStub.firstCall.args[1] + + t.true(emitStub.calledOnce) + t.is(emitted.type, 'command') + t.deepEqual(emitted.body, msg.body) + t.deepEqual(emitted.meta, msg.meta) +}) + +test('Receiver.receiveAction()', t => { + let receiver = new Receiver() + let msg = getMockInteractiveMessage() + let emitStub = sinon.stub(receiver, 'emit') + + receiver.receiveAction(msg.body) + + const emitted = emitStub.firstCall.args[1] + + t.true(emitStub.calledOnce) + t.is(emitted.type, 'action') + t.deepEqual(emitted.body, msg.body) + t.deepEqual(emitted.meta, msg.meta) +}) + +test('Receiver.receiveOption()', t => { + let receiver = new Receiver() + let msg = getMockInteractiveMessage() + let emitStub = sinon.stub(receiver, 'emit') + + receiver.receiveOptions(msg.body) + + const emitted = emitStub.firstCall.args[1] + + t.true(emitStub.calledOnce) + t.is(emitted.type, 'options') + t.deepEqual(emitted.body, msg.body) + t.deepEqual(emitted.meta, msg.meta) +}) + +function getMockEventMessage () { return { type: 'event', body: { - token: 'token', + token: 'verify_token', event: { user: 'user_id', bot_id: 'bot_id', @@ -150,3 +210,45 @@ function getMockMessage () { } } } + +function getMockCommandMessage () { + return { + type: 'command', + body: { + token: 'verify_token', + team_id: 'team_id', + channel_id: 'channel_id', + user_id: 'user_id' + }, + meta: { + verify_token: 'verify_token', + user_id: 'user_id', + channel_id: 'channel_id', + team_id: 'team_id' + } + } +} + +function getMockInteractiveMessage () { + return { + type: 'action', + body: { + token: 'verify_token', + user: { + id: 'user_id' + }, + channel: { + id: 'channel_id' + }, + team: { + id: 'team_id' + } + }, + meta: { + verify_token: 'verify_token', + user_id: 'user_id', + channel_id: 'channel_id', + team_id: 'team_id' + } + } +} diff --git a/test/slapp.test.js b/test/slapp.test.js index 336018241..6fe2b4b74 100644 --- a/test/slapp.test.js +++ b/test/slapp.test.js @@ -97,6 +97,42 @@ test('Slapp.attachToExpress()', t => { t.true(stub.calledOnce) }) +test('Slapp.receiveEvent()', t => { + let app = new Slapp({ context }) + let stub = sinon.stub(app.receiver, 'emit') + + app.receiveEvent({}) + + t.true(stub.calledOnce) +}) + +test('Slapp.receiveCommand()', t => { + let app = new Slapp({ context }) + let stub = sinon.stub(app.receiver, 'emit') + + app.receiveCommand({}) + + t.true(stub.calledOnce) +}) + +test('Slapp.receiveAction()', t => { + let app = new Slapp({ context }) + let stub = sinon.stub(app.receiver, 'emit') + + app.receiveAction({}) + + t.true(stub.calledOnce) +}) + +test('Slapp.receiveOptions()', t => { + let app = new Slapp({ context }) + let stub = sinon.stub(app.receiver, 'emit') + + app.receiveOptions({}) + + t.true(stub.calledOnce) +}) + test('Slapp.route()', t => { let app = new Slapp({ context }) let key = 'routeKey'