From fa41a5203b9d553adff7f84112985b45f7b971ae Mon Sep 17 00:00:00 2001 From: dbeard Date: Tue, 4 Dec 2018 10:28:51 -0800 Subject: [PATCH 1/4] Implement file_shared event --- src/bot.coffee | 14 +++++++++++++- src/client.coffee | 1 + src/extensions.coffee | 26 +++++++++++++++++++++++++- src/message.coffee | 14 ++++++++++++++ 4 files changed, 53 insertions(+), 2 deletions(-) diff --git a/src/bot.coffee b/src/bot.coffee index e079ee3e..a44ba84a 100644 --- a/src/bot.coffee +++ b/src/bot.coffee @@ -1,5 +1,5 @@ {Adapter, TextMessage, EnterMessage, LeaveMessage, TopicMessage, CatchAllMessage} = require.main.require "hubot" -{SlackTextMessage, ReactionMessage, PresenceMessage} = require "./message" +{SlackTextMessage, ReactionMessage, PresenceMessage, FileSharedMessage} = require "./message" SlackClient = require "./client" pkg = require "../package" @@ -287,6 +287,18 @@ class SlackBot extends Adapter @robot.logger.debug "Received presence update message for users: #{u.id for u in users} with status: #{event.presence}" @receive new PresenceMessage(users, event.presence) + + else if event.type is "file_shared" + + # Once again Hubot expects all user objects to have a room property that is used in the envelope for the message + # after it is received. If the reaction is to a message, then the `event.item.channel` contain a conversation ID. + # Otherwise reactions can be on files and file comments, which are "global" and aren't contained in a + # conversation. In that situation we fallback to an empty string. + user.room = event.channel_id + + @robot.logger.debug "Received file_shared message from: #{user.id}, file_id: #{event.file_id}" + @receive new FileSharedMessage(user, event.file_id, event.event_ts) + # NOTE: we may want to wrap all other incoming events as a generic Message # else diff --git a/src/client.coffee b/src/client.coffee index 66916ead..fe294956 100644 --- a/src/client.coffee +++ b/src/client.coffee @@ -54,6 +54,7 @@ class SlackClient @rtm.on "presence_change", @eventWrapper, this @rtm.on "member_joined_channel", @eventWrapper, this @rtm.on "member_left_channel", @eventWrapper, this + @rtm.on "file_shared", @eventWrapper, this @rtm.on "user_change", @updateUserInBrain, this @eventHandler = undefined diff --git a/src/extensions.coffee b/src/extensions.coffee index 6b2998c9..80ea9bb3 100644 --- a/src/extensions.coffee +++ b/src/extensions.coffee @@ -1,5 +1,5 @@ {Robot} = require.main.require "hubot" -{ReactionMessage, PresenceMessage} = require "./message" +{ReactionMessage, PresenceMessage, FileSharedMessage} = require "./message" ###* # Adds a Listener for ReactionMessages with the provided matcher, options, and callback @@ -66,5 +66,29 @@ Robot::presenceChange = (matcher, options, callback) -> options = matcher @listen matchPresence, options, callback + +###* +# Adds a Listener for FileSharedMessages with the provided matcher, options, and callback +# +# @public +# @param {Function} [matcher] - a function to determine if the listener should run. must return something +# truthy if it should and that value with be available on `response.match`. +# @param {Object} [options] - an object of additional parameters keyed on extension name. +# @param {Function} callback - a function that is called with a Response object if the matcher function returns true +### +Robot::fileShared = (matcher, options, callback) -> + matchFileShare = (msg) -> msg instanceof FileSharedMessage + + if not options and not callback + return @listen matchFileShare, matcher + + else if matcher instanceof Function + matchFileShare = (msg) -> msg instanceof FileSharedMessage && matcher(msg) + + else + callback = options + options = matcher + + @listen matchFileShare, options, callback # NOTE: extend Response type with a method for creating a new thread from the incoming message diff --git a/src/message.coffee b/src/message.coffee index 11073d20..2dc02890 100644 --- a/src/message.coffee +++ b/src/message.coffee @@ -20,6 +20,19 @@ class ReactionMessage extends Message constructor: (@type, @user, @reaction, @item_user, @item, @event_ts) -> super @user @type = @type.replace("reaction_", "") + +class FileSharedMessage extends Message + + ###* + # Represents a message generated by an file_shared event + # + # @constructor + # @param {User} user - A User instance that reacted to the item. + # @param {string} file_id - A String identifying the file_id of the file that was shared. + # @param {string} event_ts - A String of the file_shared event timestamp. + ### + constructor: (@user, @file_id, @event_ts) -> + super @user class PresenceMessage extends Message @@ -249,3 +262,4 @@ class SlackTextMessage extends TextMessage exports.SlackTextMessage = SlackTextMessage exports.ReactionMessage = ReactionMessage exports.PresenceMessage = PresenceMessage +exports.FileSharedMessage = FileSharedMessage \ No newline at end of file From 066c633084b68aab17b1568da315905e64ece967 Mon Sep 17 00:00:00 2001 From: dbeard Date: Tue, 4 Dec 2018 10:44:01 -0800 Subject: [PATCH 2/4] Add tests --- test/bot.coffee | 19 ++++++++++++++++++- test/stubs.coffee | 1 + 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/test/bot.coffee b/test/bot.coffee index f1633f72..88450b50 100644 --- a/test/bot.coffee +++ b/test/bot.coffee @@ -1,7 +1,7 @@ should = require 'should' chai = require 'chai' { EnterMessage, LeaveMessage, TopicMessage, CatchAllMessage, Robot } = require.main.require 'hubot' -{ SlackTextMessage, ReactionMessage, PresenceMessage } = require '../src/message' +{ SlackTextMessage, ReactionMessage, PresenceMessage, FileSharedMessage } = require '../src/message' SlackClient = require '../src/client' _ = require 'lodash' @@ -22,6 +22,13 @@ describe 'Adapter', -> # This is a sanity check to ensure the @slackbot.robot stub is proper. @slackbot.robot.listen.should.be.an.instanceOf(Function).with.lengthOf(3) @slackbot.robot.presenceChange.should.be.an.instanceOf(Function).with.lengthOf(3) + + it 'Should add the `fileShared` method to the hubot `Robot` prototype', -> + Robot.prototype.fileShared.should.be.an.instanceOf(Function).with.lengthOf(3) + + # This is a sanity check to ensure the @slackbot.robot stub is proper. + @slackbot.robot.listen.should.be.an.instanceOf(Function).with.lengthOf(3) + @slackbot.robot.fileShared.should.be.an.instanceOf(Function).with.lengthOf(3) describe 'Connect', -> it 'Should connect successfully', -> @@ -315,6 +322,16 @@ describe 'Handling incoming messages', -> should.equal @stubs._received.item_user.id, @stubs.self.id should.equal @stubs._received.type, 'added' should.equal @stubs._received.reaction, 'thumbsup' + + it 'Should handle file_shared events as envisioned', -> + fileMessage = { + type: 'file_shared', user: @stubs.user, + file_id: 'F2147483862', event_ts: '1360782804.083113' + } + @slackbot.eventHandler fileMessage + should.equal (@stubs._received instanceof FileSharedMessage), true + should.equal @stubs._received.user.id, @stubs.user.id + should.equal @stubs._received.file_id, 'F2147483862' describe 'Robot.react DEPRECATED', -> before -> diff --git a/test/stubs.coffee b/test/stubs.coffee index 855c9124..5e889da9 100644 --- a/test/stubs.coffee +++ b/test/stubs.coffee @@ -247,6 +247,7 @@ beforeEach -> robot.react = Robot.prototype.react.bind(robot) robot.hearReaction = Robot.prototype.hearReaction.bind(robot) robot.presenceChange = Robot.prototype.presenceChange.bind(robot) + robot.fileShared = Robot.prototype.fileShared.bind(robot) robot @stubs.callback = do -> return "done" From 74666333dbfcff7b6c4321aa1d5a27a0bd15c7a1 Mon Sep 17 00:00:00 2001 From: dbeard Date: Sun, 16 Dec 2018 13:21:34 -0800 Subject: [PATCH 3/4] Add explicit tests for extension fileShared + matcher --- test/bot.coffee | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/test/bot.coffee b/test/bot.coffee index 88450b50..dd349a43 100644 --- a/test/bot.coffee +++ b/test/bot.coffee @@ -379,6 +379,48 @@ describe 'Robot.react DEPRECATED', -> @slackbot.robot.react matcher, @handleReaction listener = @slackbot.robot.listeners.shift() listener.matcher(@reactionMessage).should.be.false + +describe 'Robot.fileShared', -> + before -> + user = { id: @stubs.user.id, room: @stubs.channel.id } + @fileSharedMessage = { type: "file_shared", file_id: "F2147483862", file: { "id": "F2147483862"} } + @handleFileShared = (msg) -> "#{msg.file_id} shared" + + it 'Should register a Listener with callback only', -> + @slackbot.robot.fileShared @handleFileShared + listener = @slackbot.robot.listeners.shift() + listener.matcher(@fileSharedMessage).should.be.true + listener.options.should.eql({id: null}) + listener.callback(@fileSharedMessage).should.eql('F2147483862 shared') + + it 'Should register a Listener with opts and callback', -> + @slackbot.robot.fileShared {id: 'foobar'}, @handleFileShared + listener = @slackbot.robot.listeners.shift() + listener.matcher(@fileSharedMessage).should.be.true + listener.options.should.eql({id: 'foobar'}) + listener.callback(@fileSharedMessage).should.eql('F2147483862 shared') + + it 'Should register a Listener with matcher and callback', -> + matcher = (msg) -> msg.file_id == 'F2147483862' + @slackbot.robot.fileShared matcher, @handleFileShared + listener = @slackbot.robot.listeners.shift() + listener.matcher(@fileSharedMessage).should.be.true + listener.options.should.eql({id: null}) + listener.callback(@fileSharedMessage).should.eql('F2147483862 shared') + + it 'Should register a Listener with matcher, opts, and callback', -> + matcher = (msg) -> msg.file_id == 'F2147483862' + @slackbot.robot.fileShared matcher, {id: 'foobar'}, @handleFileShared + listener = @slackbot.robot.listeners.shift() + listener.matcher(@fileSharedMessage).should.be.true + listener.options.should.eql({id: 'foobar'}) + listener.callback(@fileSharedMessage).should.eql('F2147483862 shared') + + it 'Should register a Listener that does not match the ReactionMessage', -> + matcher = (msg) -> msg.file_id == 'J12387ALDFK' + @slackbot.robot.fileShared matcher, @handleFileShared + listener = @slackbot.robot.listeners.shift() + listener.matcher(@fileSharedMessage).should.be.false describe 'Robot.hearReaction', -> before -> From 4d93e35b28c4547f25489a850f36d97643a72491 Mon Sep 17 00:00:00 2001 From: dbeard Date: Fri, 21 Dec 2018 10:11:54 -0800 Subject: [PATCH 4/4] Address PR feedback --- test/bot.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/bot.coffee b/test/bot.coffee index fa1b7a44..8cbc8c52 100644 --- a/test/bot.coffee +++ b/test/bot.coffee @@ -382,7 +382,7 @@ describe 'Robot.react DEPRECATED', -> describe 'Robot.fileShared', -> before -> user = { id: @stubs.user.id, room: @stubs.channel.id } - @fileSharedMessage = { type: "file_shared", file_id: "F2147483862", file: { "id": "F2147483862"} } + @fileSharedMessage = new FileSharedMessage(user, "F2147483862", '1360782804.083113') @handleFileShared = (msg) -> "#{msg.file_id} shared" it 'Should register a Listener with callback only', ->