Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Features: sendFile and sendHtml #270

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,27 @@ Bonus: Add a notification hook to Heroku so a notification is sent to a room whe
robot.messageRoom("[email protected]", "message");
```

## Features specific to hubot-hipchat

In addition to the response methods provided by the normal hubot, such as ```resp.send```, hubot-hipchat provides these hipchat specific functions:
* ```resp.sendHtml``` takes any number of strings and sticks them together as one html message post. For example, you can send a hyperlink by doing this:
```
robot.respond /send hyperlink/i, (resp)->
resp.sendHtml "<a href='https://hipchat.com'>hipchat site</a>"
```

* ```resp.sendFile``` takes an object that contains either file data or a path to a file along with other file info and posts the file to a room with an optional message:
```
robot.respond /send file/i, (resp) ->
file_info =
name : "the name people will see in the room"
path : 'path/to/file.text'
type: "text" # required
msg : "optional message to post when file is uploaded"

resp.sendFile file_info
```

## Adapter configuration

This adapter uses the following environment variables:
Expand Down Expand Up @@ -135,6 +156,10 @@ Optional. Set to `debug` to enable detailed debug logging.

Optional. Seting to `false` will prevent the HipChat adapter from auto-reconnecting if it detects a server error or disconnection.

### HUBOT\_HIPCHAT\_TOKEN

Optional. Set to the value of an API token for HipChat. Generate an API token [here](https://cs10.hipchat.com/account/api) (and give it the necessary scopes to post in rooms, the easiest way is just give it access to all scopes). This needs to be set in order to use the sendFile and sendHtml methods.

## Running locally

To run locally on OSX or Linux you'll need to set the required environment variables and run the `bin/hubot` script. An example script to run the bot might look like:
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@
"license": "MIT",
"main": "./src/hipchat",
"engines": {
"node" : "~0.12.2"
"node": "~0.12.2"
},
"dependencies": {
"mime": "^1.3.4",
"node-xmpp-client": "^2.1.0",
"request": "^2.67.0",
"rsvp": "~1.2.0",
"underscore": "~1.4.4"
}
Expand Down
157 changes: 128 additions & 29 deletions src/hipchat.coffee
Original file line number Diff line number Diff line change
@@ -1,52 +1,142 @@
{Adapter, TextMessage, EnterMessage, LeaveMessage, TopicMessage, User} = require "hubot"
fs = require "fs"
HTTPS = require "https"
{Adapter, TextMessage, EnterMessage, LeaveMessage, TopicMessage, User} = require "hubot"
{inspect} = require "util"
requestLib = require "request" # requestLib to avoid confusion with adapter's request method
mime = require "mime"
Connector = require "./connector"
promise = require "./promises"
HipChatResponse = require './response'

class HipChat extends Adapter

constructor: (robot) ->
super robot
@logger = robot.logger
@room_endpoint = "http://www.hipchat.com/v2/room"
@robot.Response = HipChatResponse
reconnectTimer = null

emote: (envelope, strings...) ->
@send envelope, strings.map((str) -> "/me #{str}")...

send: (envelope, strings...) ->
extractJid: (envelope) ->
{user, room} = envelope
user = envelope if not user # pre-2.4.2 style
# most common case - we're replying to a user in a room or 1-1
user?.reply_to or
# allows user objects to be passed in
user?.jid or
if user?.search?(/@/) >= 0
user # allows user to be a jid string
else
room # this will happen if someone uses robot.messageRoom(jid, ...)

target_jid =
# most common case - we're replying to a user in a room or 1-1
user?.reply_to or
# allows user objects to be passed in
user?.jid or
if user?.search?(/@/) >= 0
user # allows user to be a jid string
else
room # this will happen if someone uses robot.messageRoom(jid, ...)
send: (envelope, strings...) ->

target_jid = @extractJid(envelope)

if not target_jid
return @logger.error "ERROR: Not sure who to send to: envelope=#{inspect envelope}"

for str in strings
@connector.message target_jid, str

sendHtml: (envelope, strings...) ->
target_jid = @extractJid(envelope)

if not target_jid
return @logger.error "Not sure who to send html message to: envelope=#{inspect envelope}"

if not @options.token
return @logger.error "Must set HUBOT_HIPCHAT_TOKEN to send html messages"

room_id = @room_map[target_jid].id
fullMsg = strings.join('')

params =
url: "#{@room_endpoint}/#{room_id}/notification"
headers :
'content-type' : 'text/html'
auth:
bearer : @options.token
body: fullMsg

requestLib.post params, (err,resp,body) =>
if err || resp.statusCode >= 400
return @logger.error "HipChat API error: #{resp.statusCode}"

# Send a file from hubot
# file_info =
# name : the name to share the file with
# path : send file from this path (a string)
# data : send this base64 encoded data as a file
# type (required) : the type of the file (text, pdf, json, csv etc...)
# msg (optional) : a simple text msg to be posted along with the file
sendFile: (envelope, file_info) ->
target_jid = @extractJid(envelope)

if not target_jid
return @logger.error "Not sure who to send file to: envelope=#{inspect envelope}"

if not @options.token
return @logger.error "Must set HUBOT_HIPCHAT_TOKEN to send html messages"

room_id = @room_map[target_jid].id
url = "#{@room_endpoint}/#{room_id}/share/file"
mimeType = mime.lookup(file_info.type)
ext = mime.extension(mimeType)

if not file_info.type || not mimeType
return @logger.error "A valid type must be provided to sendFile. Type was: #{file_info.type}"

if not file_info.msg
file_info.msg = ''

if file_info.path
fs.readFile file_info.path, (err, data) =>
if err
return @logger.error "File Read Error: could not read from file path: #{file_info.path}"

@sendMultipart url, file_info.name + '.' + ext, data, mimeType, file_info.msg

else if file_info.data
@sendMultipart url, file_info.name + '.' + ext, file_info.data, mimeType, file_info.msg

else
return @logger.error "Must specify either data or path for sendFile"

sendMultipart: (path, name, data, mimeType, msg) ->

# Must have filename="name" etc... in double quotes not single
quotedName = '"' + name + '"'
params =
method: 'POST'
url: path
auth:
bearer: @options.token
multipart: [
{
"Content-Type": "application/json; charset UTF-8",
"Content-Disposition": 'attachment; name="metadata"',
"body": JSON.stringify "message": msg
}
,
{
"Content-Type": "file/" + mimeType,
"Content-Disposition": 'attachment; name="file"; filename=' + quotedName,
"body": data
}
]

requestLib params, (err, resp, body) =>
if resp.statusCode >= 400
return @logger.error "HipChat API errror: #{resp.statusCode}"


topic: (envelope, message) ->
{user, room} = envelope
user = envelope if not user # pre-2.4.2 style

target_jid =
# most common case - we're replying to a user in a room or 1-1
user?.reply_to or
# allows user objects to be passed in
user?.jid or
if user?.search?(/@/) >= 0
user # allows user to be a jid string
else
room # this will happen if someone uses robot.messageRoom(jid, ...)
target_jid = extractJid(envelope)

if not target_jid
return @logger.error "ERROR: Not sure who to send to: envelope=#{inspect envelope}"
Expand Down Expand Up @@ -165,9 +255,17 @@ class HipChat extends Adapter
init
.done (users) =>
saveUsers(users)
# Join requested rooms
if @options.rooms is "All" or @options.rooms is "@All"
connector.getRooms (err, rooms, stanza) =>

connector.getRooms (err, rooms, stanza) =>

# Save room data to make api calls
if rooms
@room_map = {}
for room in rooms
@room_map[room.jid] = room

# Join all rooms
if @options.rooms is "All" or @options.rooms is "@All"
if rooms
for room in rooms
if [email protected]_join_public && room.guest_url != ''
Expand All @@ -176,10 +274,11 @@ class HipChat extends Adapter
joinRoom(room.jid)
else
@logger.error "Can't list rooms: #{errmsg err}"
# Join all rooms
else
for room_jid in @options.rooms.split ","
joinRoom(room_jid)
# Join requested rooms
else
for room_jid in @options.rooms.split ","
joinRoom(room_jid)

.fail (err) =>
@logger.error "Can't list users: #{errmsg err}" if err

Expand Down
11 changes: 11 additions & 0 deletions src/response.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{Response} = require 'hubot'

class HipChatResponse extends Response

sendFile: (file_info) ->
@robot.adapter.sendFile(@envelope, file_info)

sendHtml: (strings...) ->
@robot.adapter.sendHtml(@envelope, strings...)

module.exports = HipChatResponse