diff --git a/.travis.yml b/.travis.yml index 6e7834887..54c394525 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ services: - docker language: node_js node_js: -- '0.10' +- '4.8.3' before_install: - npm install -g npm@'>=2.13.5' deploy: diff --git a/Dockerfile b/Dockerfile index d0bbe9967..a896796c8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:0.12.4 +FROM node:4.8.3 MAINTAINER Rocket.Chat Team RUN npm install -g coffee-script yo generator-hubot && \ diff --git a/README.md b/README.md index 8a1ab8cd8..ed60090be 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Feel free to join us in the [#hubot](https://demo.rocket.chat/channel/hubot) cha The latest version of the adapter is only compatible with 0.37.1 and higher of Rocket.Chat Server. -If you are using Rocket.Chat 0.35.0 or earilier, please use v0.1.4 of the adapter. (releases between 0.35.0 and 0.37.1 are not recommended for hubot operations) +If you are using Rocket.Chat 0.35.0 or earlier, please use v0.1.4 of the adapter. (releases between 0.35.0 and 0.37.1 are not recommended for hubot operations) #### NOTE If you want to integrate Rocket.Chat with GitHub or GitLab. Make sure you visit the [Rocket.Chat.Ops](https://github.com/RocketChat/Rocket.Chat.Ops) project before starting. We already have many scripts that add webhook events and access GitHub/GitLab APIs. You can easily extend these scripts for your custom application. @@ -162,7 +162,7 @@ Environment Variable | Description ROCKETCHAT_URL | the URL where Rocket.Chat is running, can be specified as `host:port` or `http://host:port` or `https://host:port`. If you are using `https://`, you **MUST** setup websocket pass-through on your reverse proxy (NGINX, and so on) with a valid certificate (not self-signed). Directly accessing Rocket.Chat without a reverse proxy via `https://` is not possible. ROCKETCHAT_USER | the bot user's name. It must be a registered user on your Rocket.Chat server, and the user must be granted `bot` role via Rocket.Chat's administrator's panel (note that this will also be the name that you can summon the bot with) ROCKETCHAT_PASSWORD | the bot user's password -ROCKETCHAT_AUTH | defaults to 'password' if undefinied, or set to 'ldap' if your use LDAP accounts for bots. +ROCKETCHAT_AUTH | defaults to 'password' if undefined, or set to 'ldap' if your use LDAP accounts for bots. ROCKETCHAT_ROOM | the channel/channels names the bot should listen to message from. This can be comma separated list. LISTEN_ON_ALL_PUBLIC | if 'true' then bot will listen and respond to messages from all public channels, as well as respond to direct messages. Default to 'false'. ROCKETCHAT_ROOM should be set to empty (with `ROCKETCHAT_ROOM=''` ) when using `LISTEN_ON_ALL_PUBLIC`. *IMPORTANT NOTE*: This option also allows the bot to listen and respond to messages _from all newly created private groups_ that the bot's user has been added as a member. RESPOND_TO_DM | if 'true' then bot will listen and respond to direct messages. When setting the option to 'true', be sure to also set ROCKETCHAT_ROOM. This option needs not be set if you are including LISTEN_ON_ALL_PUBLIC. Default is 'false'. @@ -194,7 +194,7 @@ And: ``` rocketbot help ``` -The example bot under `scripts` directory respeonds to: +The example bot under `scripts` directory responds to: ``` rocketbot report status ``` @@ -295,6 +295,6 @@ Once received, the bot: Q: The architecture of hubot-rocketchat looks interesting, can you tell me more about it? -A: Sure, it is based on hubot-meteorchat. hubot-meteorchat is the hubot integration project for Meteor based chats and real-time messaging systems. Its driver based architecture simplifies creation and cusotmization of adapter for new systems. For example, the hubot-rocketchat integration is just hubot-meteorchat + Rocket.Chat driver. +A: Sure, it is based on hubot-meteorchat. hubot-meteorchat is the hubot integration project for Meteor based chats and real-time messaging systems. Its driver based architecture simplifies creation and customization of adapter for new systems. For example, the hubot-rocketchat integration is just hubot-meteorchat + Rocket.Chat driver. Learn more about hubot-meteorchat and other available drivers [at this link](https://github.com/Sing-Li/hubot-meteorchat). diff --git a/package.json b/package.json index 72abbeddc..e87326e2e 100644 --- a/package.json +++ b/package.json @@ -33,13 +33,13 @@ "email": "support@rocket.chat" }, "devDependencies": { - "coffee-script": "~1.7.1" + "coffee-script": "~1.12.6" }, "main": "./src/rocketchat", "dependencies": { "asteroid": "https://github.com/RocketChat/asteroid.git", "parent-require": "^1.0.0", - "q": "^1.4.1", - "lru-cache": "~4.0.0" + "q": "^1.5.0", + "lru-cache": "~4.0.2" } } diff --git a/src/rocketchat.coffee b/src/rocketchat.coffee index 1e6991b5a..40dacc9f4 100644 --- a/src/rocketchat.coffee +++ b/src/rocketchat.coffee @@ -21,6 +21,7 @@ RocketChatUser = process.env.ROCKETCHAT_USER or "hubot" RocketChatPassword = process.env.ROCKETCHAT_PASSWORD or "password" ListenOnAllPublicRooms = (process.env.LISTEN_ON_ALL_PUBLIC or "false").toLowerCase() is 'true' RespondToDirectMessage = (process.env.RESPOND_TO_DM or "false").toLowerCase() is 'true' +RespondToLivechatMessage = (process.env.RESPOND_TO_LIVECHAT or "false").toLowerCase() is "true" RespondToEditedMessage = (process.env.RESPOND_TO_EDITED or "false").toLowerCase() is 'true' SSLEnabled = "false" @@ -84,6 +85,7 @@ class RocketChatBotAdapter extends Adapter @robot.logger.error "If joining GENERAL please make sure its using all caps." @robot.logger.error "If using LDAP, turn off LDAP, and turn on general user registration with email verification off." + process.exit 1 #hack to make hubot die on login error to fix #203 throw loginErr #rethrow to exit the chain ) # Get room IDS @@ -141,7 +143,12 @@ class RocketChatBotAdapter extends Adapter if isDM and not RespondToDirectMessage return - if not isDM and not messageOptions.roomParticipant and not ListenOnAllPublicRooms + isLC = messageOptions.roomType is 'l' + + if isLC and not RespondToLivechatMessage + return + + if not isDM and not messageOptions.roomParticipant and not ListenOnAllPublicRooms and not RespondToLivechatMessage return curts = new Date(newmsg.ts.$date) @@ -155,13 +162,15 @@ class RocketChatBotAdapter extends Adapter @lastts = curts user = @robot.brain.userForId newmsg.u._id, name: newmsg.u.username, alias: newmsg.alias - user.room = newmsg.rid - if newmsg.t is 'uj' - @robot.receive new EnterMessage user, null, newmsg._id - else + @chatdriver.getRoomName(newmsg.rid).then((roomName)=> + user.room = roomName + user.roomID = newmsg.rid + if newmsg.t is 'uj' + @robot.receive new EnterMessage user, null, newmsg._id + else # check for the presence of attachments in the message - if newmsg.file? and newmsg.attachments.length + if newmsg.attachments? and newmsg.attachments.length attachment = newmsg.attachments[0] if attachment.image_url? @@ -174,16 +183,20 @@ class RocketChatBotAdapter extends Adapter attachment.link = "#{RocketChatURL}#{attachment.video_url}" attachment.type = 'video' - message = new AttachmentMessage user, attachment, attachment.title, newmsg._id + message = new AttachmentMessage user, attachment, newmsg.msg, newmsg._id else message = new TextMessage user, newmsg.msg, newmsg._id startOfText = if message.text.indexOf('@') == 0 then 1 else 0 robotIsNamed = message.text.indexOf(@robot.name) == startOfText || message.text.indexOf(@robot.alias) == startOfText - if isDM and not robotIsNamed + if (isDM or isLC) and not robotIsNamed message.text = "#{ @robot.name } #{ message.text }" @robot.receive message @robot.logger.info "Message sent to hubot brain." + ).catch((roomErr) => + @robot.logger.error "Unable to get room name: #{JSON.stringify(roomErr)} Reason: #{roomErr.reason}" + throw roomErr + ) ) .then(() => @emit 'connected' diff --git a/src/rocketchat_driver.coffee b/src/rocketchat_driver.coffee index e0e42a477..177bb0558 100644 --- a/src/rocketchat_driver.coffee +++ b/src/rocketchat_driver.coffee @@ -8,22 +8,23 @@ LRU = require('lru-cache') # TODO: need to grab these values from process.env[] _msgsubtopic = 'stream-room-messages' # 'messages' -_msgsublimit = 10 # this is not actually used right now +_msgsublimit = 10 # this is not actually used right now _messageCollection = 'stream-room-messages' # room id cache _roomCacheSize = parseInt(process.env.ROOM_ID_CACHE_SIZE) || 10 _directMessageRoomCacheSize = parseInt(process.env.DM_ROOM_ID_CACHE_SIZE) || 100 _cacheMaxAge = parseInt(process.env.ROOM_ID_CACHE_MAX_AGE) || 300 -_roomIdCache = LRU( max: _roomCacheSize, maxAge: 1000 * _cacheMaxAge ) -_directMessageRoomIdCache = LRU( max: _directMessageRoomCacheSize, maxAge: 1000 * _cacheMaxAge ) +_roomIdCache = LRU(max: _roomCacheSize, maxAge: 1000 * _cacheMaxAge) +_directMessageRoomIdCache = LRU(max: _directMessageRoomCacheSize, maxAge: 1000 * _cacheMaxAge) +_roomNameCache = LRU(max: _roomCacheSize, maxAge: 1000 * _cacheMaxAge) # driver specific to Rocketchat hubot integration # plugs into generic rocketchatbotadapter class RocketChatDriver constructor: (url, ssl, @logger, cb) -> - if ssl is 'true' + if ssl is 'true' sslenable = true else sslenable = false @@ -39,6 +40,9 @@ class RocketChatDriver getRoomId: (room) => @tryCache _roomIdCache, 'getRoomIdByNameOrId', room, 'Room ID' + getRoomName: (room) => + @tryCache _roomNameCache, 'getRoomNameById', room, 'Room Name' + getDirectMessageRoomId: (username) => @tryCache _directMessageRoomIdCache, 'createDirectMessage', username, 'DM Room ID' @@ -79,7 +83,7 @@ class RocketChatDriver sendMessageByRoomId: (content, roomid) => message = @prepareMessage content, roomid - @asteroid.call('sendMessage', message) + Q(@asteroid.call('sendMessage', message)) .then (result)-> @logger.debug('[sendMessage] Success:', result) .catch (error) -> @@ -95,7 +99,8 @@ class RocketChatDriver bot: true, groupable: false, alias: message.alias, - avatar: message.avatar + avatar: message.avatar, + emoji: message.emoji, }) login: (username, password) =>