diff --git a/.editorconfig b/.editorconfig index b0d7fd91b..ff7788e28 100644 --- a/.editorconfig +++ b/.editorconfig @@ -2,7 +2,7 @@ root = true [*] -indent_style = tab +indent_style = space end_of_line = lf charset = utf-8 trim_trailing_whitespace = true diff --git a/Dockerfile b/Dockerfile index 7e510621d..5f304c7e3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,6 +11,7 @@ WORKDIR /home/hubot ENV BOT_NAME "rocketbot" ENV BOT_OWNER "No owner specified" ENV BOT_DESC "Hubot with rocketbot adapter" +ENV HUBOT_LOG_LEVEL "error" ENV EXTERNAL_SCRIPTS=hubot-diagnostics,hubot-help,hubot-google-images,hubot-google-translate,hubot-pugme,hubot-maps,hubot-rules,hubot-shipit diff --git a/README.md b/README.md index f9d28353f..659a6b8fe 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ v1.x.x versions of the adapter is only compatible with 0.37.1 and higher of Rock 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. +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. ### For v2.x.x of the Adapter (Hubot v3 and ES6 support) @@ -48,8 +48,8 @@ You can quickly spin up a docker image with: ``` docker run -it -e ROCKETCHAT_URL=: \ - -e ROCKETCHAT_ROOM='' \ - -e LISTEN_ON_ALL_PUBLIC=true \ + -e ROCKETCHAT_ROOM='general' \ + -e RESPOND_TO_DM=true \ -e ROCKETCHAT_USER=bot \ -e ROCKETCHAT_PASSWORD=bot \ -e ROCKETCHAT_AUTH=password \ @@ -64,8 +64,8 @@ If you want to include your own custom scripts you can by doing: ``` docker run -it -e ROCKETCHAT_URL=: \ - -e ROCKETCHAT_ROOM='' \ - -e LISTEN_ON_ALL_PUBLIC=true \ + -e ROCKETCHAT_ROOM='general' \ + -e RESPOND_TO_DM=true \ -e ROCKETCHAT_USER=bot \ -e ROCKETCHAT_PASSWORD=bot \ -e ROCKETCHAT_AUTH=password \ @@ -81,7 +81,7 @@ An admin user is required to create the account for the bot to login to. 1. From **Administration** > **Users** menu 2. Select `+` to make a new user -3. Enter *Name*, *Username*, *Email* (tick verified) and *Passwword* +3. Enter *Name*, *Username*, *Email* (tick verified) and *Password* 4. Disable *Require password change* 5. Select `bot` from role selection and click *Add Role* 6. Disable *Join default channels* recommended, to avoid accidental listening @@ -122,45 +122,40 @@ adapter. ### Configuring Your Bot +[rcsdk-env]: https://github.com/rocketchat/rocket.chat.js.sdk#settings +[hubot-env]: https://hubot.github.com/docs/scripting/#environment-variables + In local development, the following can be set in an `.env` file. In production they would need to be set on server startup. +The Rocket.Chat adapter implements the Rocket.Chat Node.js SDK to load all +settings from the environment. So the following are just some of those settings, +relevant to Hubot. It has some additional configs, [documented here][rcsdk-env]. + | Env variable | Description | | ---------------------- | ----------------------------------------------------- | +| **Hubot** | A subset of relevant [Hubot env vars][hubot-env] | +| `HUBOT_ADAPTER` | Set to `rocketchat` (or pass as launch argument) | | `HUBOT_NAME` | The programmatic name for listeners | | `HUBOT_ALIAS` | An alternate name for the bot to respond to | -| `HUBOT_LOG_LEVEL` | The minimum level of logs to output | +| `HUBOT_LOG_LEVEL` | The minimum level of logs to output (error) | | `HUBOT_HTTPD` | If the bot needs to listen to or make HTTP requests | -| `HUBOT_ADAPTER`** | The platform adapter package to require on loading | +| **Rocket.Chat SDK** | A subset of relevant [SDK env vars][rcsdk-env] | | `ROCKETCHAT_URL`* | Local Rocketchat address (start before the bot) | | `ROCKETCHAT_USER`* | Name in the platform (bot user must be created first) | | `ROCKETCHAT_PASSWORD`* | Matching the credentials setup in Rocket.Chat | -| `ROCKETCHAT_ROOM` | The default room/s for the bot to listen in to | -| `LISTEN_ON_ALL_PUBLIC` | Whether the bot should be listening everywhere | +| `ROCKETCHAT_ROOM` | The default room/s for the bot to listen in to (csv) | +| `LISTEN_ON_ALL_PUBLIC` | DEPRECATED - DO NOT USE | | `RESPOND_TO_DM` | If the bot can respond privately or only in the open | | `RESPOND_TO_EDITED` | If the bot should reply / re-reply to edited messages | | `RESPOND_TO_LIVECHAT` | If the bot should respond in livechat rooms | +| `INTEGRATION_ID | Name to ID source of messages in code (e.g Hubot) | -`*` Required settings - -`**` Set to `rocketchat` to enable this adapter (or pass as launch argument) - - If you wish that your bot listen to all public rooms and all private rooms it - is joined to let the env `ROCKETCHAT_ROOM` empty like in the example above and - set the env `LISTEN_ON_ALL_PUBLIC` to true. +`*` Required settings, unless running locally with testing defaults: +- url: `localhost:3000` +- username: `bot` +- password: `pass` -The Rocket.Chat adapter implements the Rocket.Chat Node.js SDK to call server -methods and selectively cache their results. For advanced usage, you may wish -to modify defaults for the SDK using it's environment settings, documented here: -https://github.com/rocketchat/rocket.chat.js.sdk#settings - -##### Common configuration - -It is common to set up a bot to listen and respond to direct messages and all -new public channels and private groups. Use the following options: -- `LISTEN_ON_ALL_PUBLIC=true` -- `ROCKETCHAT_ROOM=''` -- *do not* specify `RESPOND_TO_DM` Be aware you *must* add the bot's user as a member of the new private group(s) before it will respond. @@ -175,8 +170,8 @@ You can quickly spin up a docker image with: ``` docker run -it -e ROCKETCHAT_URL=: \ - -e ROCKETCHAT_ROOM='' \ - -e LISTEN_ON_ALL_PUBLIC=true \ + -e ROCKETCHAT_ROOM='general' \ + -e RESPOND_TO_DM=true \ -e ROCKETCHAT_USER=bot \ -e ROCKETCHAT_PASSWORD=bot \ -e HUBOT_NAME=bot \ @@ -190,8 +185,8 @@ If you want to include your own custom scripts you can by doing: ``` docker run -it -e ROCKETCHAT_URL=: \ - -e ROCKETCHAT_ROOM='' \ - -e LISTEN_ON_ALL_PUBLIC=true \ + -e ROCKETCHAT_ROOM='general' \ + -e RESPOND_TO_DM=true \ -e ROCKETCHAT_USER=botname \ -e ROCKETCHAT_PASSWORD=botpass \ -e HUBOT_NAME=botname \ @@ -207,35 +202,13 @@ On Docker you use: `-e VAR=Value` Regular hubot via: `export VAR=Value` or add to pm2 etc -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 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 respond to direct messages. When setting the option to 'true', be sure to also set ROCKETCHAT_ROOM or LISTEN_ON_ALL_PUBLIC. Default is 'false'. -RESPOND_TO_EDITED | if 'true' then bot will respond to edited messages. Default is 'false'. -ROOM_ID_CACHE_SIZE | The maximum number of room IDs to cache. You can increase this if your bot usually sends messages to a large number of different rooms. Default value: 10 -DM_ROOM_ID_CACHE_SIZE | The maximum number of Direct Message room IDs to cache. You can increase this if your bot usually sends a large number of Direct Messages. Default value: 100 -ROOM_ID_CACHE_MAX_AGE | Room IDs and DM Room IDS are cached for this number of seconds. You can increase this value to improve performance in certain scenarios. Default value: 300 -BOT_NAME | ** Name of the bot. This is what it responds to -EXTERNAL_SCRIPTS | ** These are the npm modules it will add to hubot. -HUBOT_LOG_LEVEL | hubot log level, string [debug|info|warning|error], default: info - -** - Docker image only. -##### Configuring the Bot to listen and respond to direct messages plus all new public channels and private groups - -This is a common configuration for Rocket.Chat bot installations. - -Use the following options: - -`LISTEN_ON_ALL_PUBLIC=true` and `ROCKETCHAT_ROOM=''` and *do not* specify `RESPOND_TO_DM` - -Be aware you *must* add the bot's user as a member of the new private group(s) before it will respond. +If `ROCKETCHAT_URL` is 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. ### Verify your bot is working + Try: ``` rocketbot ping @@ -265,8 +238,8 @@ Now we start the docker container. ``` docker run -it -e ROCKETCHAT_URL=: \ - -e ROCKETCHAT_ROOM='' \ - -e LISTEN_ON_ALL_PUBLIC=true \ + -e ROCKETCHAT_ROOM='general' \ + -e RESPOND_TO_DM=true \ -e ROCKETCHAT_USER=bot \ -e ROCKETCHAT_PASSWORD=bot \ -e HUBOT_NAME=bot \ @@ -318,8 +291,8 @@ hubot: image: rocketchat/hubot-rocketchat:v0.1.4 environment: - ROCKETCHAT_URL=your-rocket-chat-instance-ip:3000 (e.g. 192.168.2.240:3000) - - ROCKETCHAT_ROOM= - - LISTEN_ON_ALL_PUBLIC=true + - ROCKETCHAT_ROOM=general + - RESPOND_TO_DM=true - ROCKETCHAT_USER=username-of-your-bot - ROCKETCHAT_PASSWORD=yourpass - BOT_NAME=bot @@ -398,8 +371,8 @@ Also be sure to remember the name you specify. This is what the bot will respon You will need to tell the adapter where your install is and what login information to use. ``` -export ROCKETCHAT_ROOM='' -export LISTEN_ON_ALL_PUBLIC=true +export ROCKETCHAT_ROOM='general' +export RESPOND_TO_DM=true export ROCKETCHAT_USER=bot export ROCKETCHAT_PASSWORD=bot export ROCKETCHAT_AUTH=password @@ -410,59 +383,26 @@ Then start with: `bin/hubot -a rocketchat` [More Info Here](https://hubot.github.com/docs/) ##### Existing install + If you already have hubot setup you can add the adapter. -By doing: `npm install hubot-rocketchat@1` +By doing: `npm install hubot-rocketchat@2` -You will need to tell the adapter where your install is and what login information to use. +You will need to tell the adapter where your install is and what login +information to use. ``` -export ROCKETCHAT_ROOM='' -export LISTEN_ON_ALL_PUBLIC=true -export ROCKETCHAT_USER=bot +export ROCKETCHAT_ROOM='general' +export RESPOND_TO_DM=true +export ROCKETCHAT_USER=rocketbot export ROCKETCHAT_PASSWORD=bot export ROCKETCHAT_AUTH=ldap ``` Then starting your bot specifying the adapter: `bin/hubot -a rocketchat` -##### Configuration Options - -Here are all of the options you can specify to configure the bot. - -On Docker you use: `-e VAR=Value` - -Regular hubot via: `export VAR=Value` or add to pm2 etc - -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 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 respond to direct messages. When setting the option to 'true', be sure to also set ROCKETCHAT_ROOM or LISTEN_ON_ALL_PUBLIC. Default is 'false'. -RESPOND_TO_EDITED | if 'true' then bot will respond to edited messages. Default is 'false'. -ROOM_ID_CACHE_SIZE | The maximum number of room IDs to cache. You can increase this if your bot usually sends messages to a large number of different rooms. Default value: 10 -DM_ROOM_ID_CACHE_SIZE | The maximum number of Direct Message room IDs to cache. You can increase this if your bot usually sends a large number of Direct Messages. Default value: 100 -ROOM_ID_CACHE_MAX_AGE | Room IDs and DM Room IDS are cached for this number of seconds. You can increase this value to improve performance in certain scenarios. Default value: 300 -BOT_NAME | ** Name of the bot. This is what it responds to -EXTERNAL_SCRIPTS | ** These are the npm modules it will add to hubot. -HUBOT_LOG_LEVEL | hubot log level, string [debug|info|warning|error], default: info - -** - Docker image only. -###### Configuring the Bot to listen and respond to direct messages plus all new public channels and private groups - -This is a common configuration for Rocket.Chat bot installations. - -Use the following options: - -`LISTEN_ON_ALL_PUBLIC=true` and `ROCKETCHAT_ROOM=''` and *do not* specify `RESPOND_TO_DM` - -Be aware you *must* add the bot's user as a member of the new private group(s) before it will respond. - #### Verify your bot is working + Try: ``` rocketbot ping @@ -472,6 +412,7 @@ And: ``` rocketbot help ``` + The example bot under `scripts` directory responds to: ``` rocketbot report status @@ -479,9 +420,11 @@ rocketbot report status ### Developers -We like to make development as easy on ourselves as possible. So passing the love on to you! +We like to make development as easy on ourselves as possible. So passing the +love on to you! #### Adapter Development + We'd love to have your help improving this adapter. PR's very welcome :smile: ##### Docker @@ -497,8 +440,8 @@ Now we start the docker container. ``` docker run -it -e ROCKETCHAT_URL=: \ - -e ROCKETCHAT_ROOM='' \ - -e LISTEN_ON_ALL_PUBLIC=true \ + -e ROCKETCHAT_ROOM='general' \ + -e RESPOND_TO_DM=true \ -e ROCKETCHAT_USER=bot \ -e ROCKETCHAT_PASSWORD=bot \ -e ROCKETCHAT_AUTH=password \ @@ -546,7 +489,6 @@ Become part of the project, just pick an issue and file a PR. The adapter code is under the `src` directory. To test modified adapter code, exit (ctrl-c) the container and run it again. - ### FAQ Q: I am not trying to stage a denial of service attack, why would I ever want to write a bot? diff --git a/Tecvinson.txt b/Tecvinson.txt new file mode 100644 index 000000000..a1da02c8d --- /dev/null +++ b/Tecvinson.txt @@ -0,0 +1,2 @@ +The program has been awesome, and I've been learning so much. I'm really looking forward to learning even more. +Thanks a lot for the opportunity and for making such a great program diff --git a/docs/snippets.md b/docs/snippets.md new file mode 100644 index 000000000..661dfd8bb --- /dev/null +++ b/docs/snippets.md @@ -0,0 +1,93 @@ +# Hubot Rocket.Chat Snippets + +Included in the adapter source are some snippets for advanced usage of Hubot and +the Rocket.Chat JS SDK (included in the adapter). They can be tested locally by +developers, but aren't included in the package published to NPM. + +The snippets documented here aren't intended for use in every instance and the +method of loading them is not recommended other than for testing these examples. +Please see https://rocket.chat/docs/bots for more general getting started tips. + +## Available Snippets + +- [Use API to check if role exists on given user](snippets/userHasRole.js) +- [Use API to clear the room history](snippets/cleanRoomHistory.js) +- [Use API to add users to Hubot's brain](snippets/syncBrainUsers.js) + +e.g. To execute one of the demos, from the adapter root in local CLI with Node: + +``` +node docs/snippets userHasRole +``` + +Then login to talk to the bot in your local Rocket.Chat instance. For this +example, try "bot is admin an admin". + +See [index.js](snippets/index.js) to learn how a mock Hubot loads the snippets. + +## Testing Environment + +Running the example snippets locally requires the same environment settings +as any bot, but it's recommended to only use them on a test instance, with +test users and rooms, similar to the approach used for testing the SDK: + +https://github.com/RocketChat/Rocket.Chat.js.SDK#installing-rocketchat + +## Snippet Patterns + +Each snippet demonstrates a specific function, which is exported for tests, +they also may include a `load` function that add commands to demonstrate the +snippet in use. The exports don't follow the normal Hubot script pattern, +which is to export a single function accepting the bot as a single argument. + +Note, many command helpers follow the pattern of returning undefined if not +successful, as opposed to throwing. This allows them to log errors and let +the bot send an apology rather than silently failing. + +## Example Snippet + +### `userHasRole` + +#### Define Function + +This example demonstrates the pattern. The function is defined outside the scope +where the bot is available, so it takes the `robot` instance as first parameter. +Then Hubot scripts would use it within the `module.exports = (robot) =>` block. + +Keeping async calls within a `try`...`catch` block allows any errors or promise +rejections to go to a single handler, which logs the error, returning undefined. + +![userHasRole function snippet](snippets/userHasRole-function.png) + +#### Load Command + +The load function adds a listener callback, providing `userHasRole` utility to +users. The RegExp pattern matching uses word boundaries `\b`, capture groups, +`(.*)` and optional characters `an?` and `\??` with start `^` or end of line +matching `$` to make sure it doesn't accidentally match on other messages. +It takes inputs from matching capture groups as arguments for `userHasRole`. + +![userHasRole load snippet](snippets/userHasRole-command.png) + +In production, it would be recommended to check that the user making the request +has permission to lookup other user's roles. This could be done like: + +```js +robot.respond(PATTERN, async (res) => { + const allowed = await userHasRole(robot, res.message.user, 'admin').catch() + if (!allowed) return res.reply(`Sorry, you can't do that.`) + ... +}) +``` + +### The Result + +![userHasRole interaction demo](snippets/userHasRole-demo.png) + +Enjoy. + +___ + +[polacode]: https://github.com/octref/polacode +[firacode]: https://github.com/tonsky/FiraCode +> Image created by [Polacode][polacode] with [Fira Code font][firacode] diff --git a/docs/snippets/cleanRoomHistory.js b/docs/snippets/cleanRoomHistory.js new file mode 100644 index 000000000..a0330716b --- /dev/null +++ b/docs/snippets/cleanRoomHistory.js @@ -0,0 +1,45 @@ +/** + * Use API to clear the room history (useful in testing, dangerous otherwise). + * Bot requires `clean-channel-history` permission. + * Test with `node docs/snippets cleanRoomHistory` from project root. + * @param {Robot} robot Hubot instance + * @param {User} user Hubot user object containing name and room + * @param {string} oldest ISO date string to clean all messages since then + * @return {boolean|undefined} If room was cleaned + */ +async function cleanRoomHistory (robot, user, oldest) { + try { + const latest = new Date().toISOString() + const roomId = user.roomID + robot.logger.info(`[cleanRoomHistory] ${user.name} cleaning room ${user.room} from ${oldest} to ${latest}`) + await robot.adapter.api.post('rooms.cleanHistory', { roomId, latest, oldest }) + return true + } catch (err) { + robot.logger.error(`[cleanRoomHistory] failed, ensure bot has \`clean-channel-history\` permission`, err) + } +} + +/** + * Add command for bot to clear the room history (requires client reload). + * e.g. "bot clr" or "@bot clear room" or "bot clr from June 3, 2018 17:30". + * @param {Robot} robot The Hubot instance + */ +function load (robot) { + robot.respond(/\b(clean room|clr)( from (.*))?(\.|!|)?$/i, async (res) => { + try { + const from = res.match[3] || 'May 19, 2015 04:36:09' // clear all if not given date + const oldest = new Date(from).toISOString() + const cleaned = await cleanRoomHistory(robot, res.message.user, oldest).catch() + if (typeof cleaned === 'undefined') { + res.reply(`Sorry, I'm afraid I can't do that.`) + } + } catch (err) { + res.reply(`That wasn't a valid date`) + } + }) +} + +module.exports = { + cleanRoomHistory, + load +} diff --git a/docs/snippets/index.js b/docs/snippets/index.js new file mode 100644 index 000000000..c28f48cae --- /dev/null +++ b/docs/snippets/index.js @@ -0,0 +1,33 @@ +/** + * Code below initialises a Hubot instance, connecting to a local Rocket.Chat. + * It is only for demonstrating the snippets in a development environment. They + * are provided as tips for advanced use cases, not finished scripts to deploy. + */ + +// Force some configs for demo +process.env.ROCKETCHAT_ROOM = 'general' +process.env.LISTEN_ON_ALL_PUBLIC = false +process.env.RESPOND_TO_EDITED = true +process.env.RESPOND_TO_DM = true +process.env.HUBOT_LOG_LEVEL = 'debug' + +// Hack solution to get Hubot to load a custom local path as adapter +const { Robot } = require('hubot') +Robot.super_.prototype.loadAdapter = function () { + this.adapter = require('../../').use(this) + this.adapterName = 'hubot-rocketchat' +} + +// Robot args --> adapterPath, adapterName, enableHttpd, botName, botAlias +const bot = new Robot(null, null, false, 'bot', 'hubot') + +// Require the snippet at the path given in loading args... +// e.g. `node docs/snippets userHasRole` +const snippetArg = process.argv[2] +try { + const snippet = require(`./${snippetArg}`) + bot.adapter.on('connected', () => snippet.load(bot)) + bot.run() +} catch (error) { + bot.logger.error(`Couldn't require snippet path: ./${snippetArg}`, error) +} diff --git a/docs/snippets/syncBrainUsers.js b/docs/snippets/syncBrainUsers.js new file mode 100644 index 000000000..263c90c8b --- /dev/null +++ b/docs/snippets/syncBrainUsers.js @@ -0,0 +1,47 @@ +/** + * Use API user helper to sync RC users with Hubot brain. + * Test with `node docs/snippets syncBrainUsers` from project root. + * @param {Robot} robot The Hubot instance + * @returns {string|undefined} List of names added + */ +async function syncBrainUsers (robot) { + try { + robot.logger.info(`[syncBrainUsers] adding all users to Hubot brain`) + const allUsers = await robot.adapter.api.users.all() + const knownUsers = robot.brain.users() + const addedUsers = [] + for (let user of allUsers) { + if (knownUsers[user._id]) continue + robot.brain.userForId(user._id, { + name: user.username, + alias: user.alias + }) + addedUsers.push(user.username) + } + return addedUsers + } catch (err) { + robot.logger.error('Could not sync user data with bot', err) + } +} + +/** + * Add command for bot to respond to requests for brain sync with added users. + * e.g. "bot sync brain users" or "@bot sync users with brain" + * @param {Robot} robot The Hubot instance + */ +function load (robot) { + robot.respond(/^(sync users with brain|sync brain users)/i, async (res) => { + const addedUsers = await syncBrainUsers(robot) + if (typeof addedUsers === 'undefined') { + res.reply(`Sorry I can't do that.`) + } else { + const names = '@' + addedUsers.join(', @').replace(/,(?!.*,)/gmi, ' and') + res.reply(`${names} were added to my brain.`) + } + }) +} + +module.exports = { + syncBrainUsers, + load +} diff --git a/docs/snippets/userHasRole-command.png b/docs/snippets/userHasRole-command.png new file mode 100644 index 000000000..7760e345f Binary files /dev/null and b/docs/snippets/userHasRole-command.png differ diff --git a/docs/snippets/userHasRole-demo.png b/docs/snippets/userHasRole-demo.png new file mode 100644 index 000000000..21f2afd94 Binary files /dev/null and b/docs/snippets/userHasRole-demo.png differ diff --git a/docs/snippets/userHasRole-function.png b/docs/snippets/userHasRole-function.png new file mode 100644 index 000000000..aa3629f11 Binary files /dev/null and b/docs/snippets/userHasRole-function.png differ diff --git a/docs/snippets/userHasRole.js b/docs/snippets/userHasRole.js new file mode 100644 index 000000000..bb86b4102 --- /dev/null +++ b/docs/snippets/userHasRole.js @@ -0,0 +1,46 @@ +/** + * Use API to check if role exists on given user. + * Bot requires `view-full-other-user-info` permission. + * Test with `node docs/snippets userHasRole` from project room. + * @param {Robot} robot Hubot instance + * @param {User} user Hubot user object containing name + * @param {string} role Role to check for in roles array + * @return {boolean|undefined} If user has the role + */ +async function userHasRole (robot, user, role) { + try { + robot.logger.info(`[userHasRole] checking if ${user.name} has ${role} role`) + const info = await robot.adapter.api.get('users.info', { username: user.name }) + if (!info.user) throw new Error('No user data returned') + if (!info.user.roles) throw new Error('User data did not include roles') + return (info.user.roles.indexOf(role) !== -1) + } catch (err) { + robot.logger.error('Could not get user data with bot, ensure it has `view-full-other-user-info` permission', err) + } +} + +/** + * Add command for bot to respond to requests for a role check on a user. + * e.g. "Hubot is admin an admin?" or "@bot is bot a bot" - both reply true. + * @param {Robot} robot The Hubot instance + */ +function load (robot) { + robot.respond(/\bis (.*) an? (.*?)\??$/i, async (res) => { + const name = res.match[1] + const role = res.match[2] + const hasRole = await userHasRole(robot, { name }, role).catch() + if (typeof hasRole === 'undefined') { + res.reply(`Sorry, I can't do that.`) + } else { + res.reply((hasRole) + ? `Yes, @${name} has the \`${role}\` role.` + : `No, @${name} does not have the \`${role}\` role.` + ) + } + }) +} + +module.exports = { + userHasRole, + load +} diff --git a/index.js b/index.js index d852e884e..7d47ad583 100644 --- a/index.js +++ b/index.js @@ -3,20 +3,7 @@ const Adapter = require.main.require('hubot/src/adapter') const Response = require.main.require('hubot/src/response') const { TextMessage, EnterMessage, LeaveMessage } = require.main.require('hubot/src/message') -const { driver } = require('@rocket.chat/sdk') - -/** Take configs from environment settings or defaults */ -const config = { - url: process.env.ROCKETCHAT_URL || 'localhost:3000', - room: process.env.ROCKETCHAT_ROOM || 'GENERAL', - user: process.env.ROCKETCHAT_USER || 'hubot', - pass: process.env.ROCKETCHAT_PASSWORD || 'password', - listenOnAllPublic: (process.env.LISTEN_ON_ALL_PUBLIC || 'false').toLowerCase() === 'true', - respondToDM: (process.env.RESPOND_TO_DM || 'false').toLowerCase() === 'true', - respondToLivechat: (process.env.RESPOND_TO_LIVECHAT || 'false').toLowerCase() === 'true', - respondToEdited: (process.env.RESPOND_TO_EDITED || 'false').toLowerCase() === 'true', - sslEnabled: (process.env.ROCKETCHAT_USESSL || 'false').toLowerCase() === 'true' -} +const { driver, api, methodCache, settings } = require('@rocket.chat/sdk') /** Extend default response with custom adapter methods */ class RocketChatResponse extends Response { @@ -46,9 +33,21 @@ class AttachmentMessage extends TextMessage { class RocketChatBotAdapter extends Adapter { run () { this.robot.logger.info(`[startup] Rocket.Chat adapter in use`) - + + // Make SDK modules available to scripts, via `adapter.` + this.driver = driver + this.methodCache = methodCache + this.api = api + this.settings = settings + // Print logs with current configs - this.startupLogs() + this.robot.logger.info(`[startup] Respond to name: ${this.robot.name}`) + this.robot.alias = (this.robot.name === settings.username || this.robot.alias) + ? this.robot.alias + : settings.username + if (this.robot.alias) { + this.robot.logger.info(`[startup] Respond to alias: ${this.robot.alias}`) + } // Overwrite Robot's response class with Rocket.Chat custom one this.robot.Response = RocketChatResponse @@ -57,29 +56,19 @@ class RocketChatBotAdapter extends Adapter { // Joins single or array of rooms by name from room setting (comma separated) // Reactive message subscription uses callback to process every stream update driver.useLog(this.robot.logger) - driver.connect({ - host: config.url, - useSsl: config.sslEnabled - }) + driver.connect() .catch((err) => { this.robot.logger.error(this.robot.logger.error(`Unable to connect: ${JSON.stringify(err)}`)) throw err }) .then(() => { - return driver.login({ username: config.user, password: config.pass }) + return driver.login() }) .catch((err) => { this.robot.logger.error(this.robot.logger.error(`Unable to login: ${JSON.stringify(err)}`)) throw err - }).then((_id) => { - this.userId = _id - return driver.joinRooms(config.room.split(',').filter((room) => (room !== ''))) }) - .catch((err) => { - this.robot.logger.error(this.robot.logger.error(`Unable to join rooms: ${JSON.stringify(err)}`)) - throw err - }) - .then((joined) => { + .then(() => { return driver.subscribeToMessages() }) .catch((err) => { @@ -99,8 +88,8 @@ class RocketChatBotAdapter extends Adapter { this.robot.logger.info('Filters passed, will receive message') // Collect required attributes from message meta - const isDM = (meta.roomType == 'd') - const isLC = (meta.roomType == 'l') + const isDM = (meta.roomType === 'd') + const isLC = (meta.roomType === 'l') const user = this.robot.brain.userForId(message.u._id, { name: message.u.username, alias: message.alias @@ -130,13 +119,13 @@ class RocketChatBotAdapter extends Adapter { if (Array.isArray(message.attachments) && message.attachments.length) { let attachment = message.attachments[0] if (attachment.image_url) { - attachment.link = `${config.url}${attachment.image_url}` + attachment.link = `${settings.host}${attachment.image_url}` attachment.type = 'image' } else if (attachment.audio_url) { - attachment.link = `${config.url}${attachment.audio_url}` + attachment.link = `${settings.host}${attachment.audio_url}` attachment.type = 'audio' } else if (attachment.video_url) { - attachment.link = `${config.url}${attachment.video_url}` + attachment.link = `${settings.host}${attachment.video_url}` attachment.type = 'video' } this.robot.logger.debug('Message type AttachmentMessage') @@ -149,10 +138,10 @@ class RocketChatBotAdapter extends Adapter { return this.robot.receive(textMessage) } - /** Send messages to user adddressed in envelope */ + /** Send messages to user addressed in envelope */ send (envelope, ...strings) { return strings.map((text) => { - if (envelope.user.roomID) driver.sendToRoomId(text, envelope.user.roomID) + if (envelope.user && envelope.user.roomID) driver.sendToRoomId(text, envelope.user.roomID) else driver.sendToRoom(text, envelope.room) }) } @@ -192,31 +181,6 @@ class RocketChatBotAdapter extends Adapter { callMethod (method, ...args) { return driver.callMethod(method, args) } - - /** Starting config print outs, split-out from logic for easy reading */ - startupLogs () { - this.robot.logger.info(`[startup] Respond to the name: ${this.robot.name}`) - this.robot.alias = (this.robot.name === config.user || this.robot.alias) ? this.robot.alias : config.user - - if (this.robot.alias) { - this.robot.logger.info(`I will also respond to my Rocket.Chat username as an alias ${this.robot.alias}`) - } - - if (!process.env.ROCKETCHAT_URL) { - this.robot.logger.warning(`No services ROCKETCHAT_URL provided to Hubot, using ${config.url}`) - } - - if (!process.env.ROCKETCHAT_ROOM) { - this.robot.logger.warning(`No services ROCKETCHAT_ROOM provided to Hubot, using ${config.room}`) - } - - if (!process.env.ROCKETCHAT_USER) { - this.robot.logger.warning(`No services ROCKETCHAT_USER provided to Hubot, using ${config.user}`) - } - - this.robot.logger.info(`[startup] Rooms specified: ${config.room}`) - } } exports.use = (robot) => new RocketChatBotAdapter(robot) - diff --git a/package-lock.json b/package-lock.json index fdaa8f409..41a19752e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,14 +5,26 @@ "requires": true, "dependencies": { "@rocket.chat/sdk": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@rocket.chat/sdk/-/sdk-0.1.0.tgz", - "integrity": "sha1-uFcXq9gf1bSFcm62tgno8bUKuBg=", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@rocket.chat/sdk/-/sdk-0.2.2.tgz", + "integrity": "sha1-JnxfMXyIOE5ZxBUd7jlP+zKvqsM=", "requires": { - "@types/lru-cache": "4.1.0", - "@types/node": "9.6.2", + "@types/lru-cache": "^4.1.0", + "@types/node": "^9.4.6", "asteroid": "github:rocketchat/asteroid#a76a53254e381f9487aa2a9be3d874c9a2df6552", - "lru-cache": "4.1.2" + "lru-cache": "^4.1.1", + "node-rest-client": "^3.1.0" + }, + "dependencies": { + "asteroid": { + "version": "github:rocketchat/asteroid#a76a53254e381f9487aa2a9be3d874c9a2df6552", + "from": "asteroid@github:rocketchat/asteroid#a76a53254e381f9487aa2a9be3d874c9a2df6552", + "requires": { + "ddp.js": "^0.5.0", + "faye-websocket": "^0.11.0", + "q": "^1.0.1" + } + } } }, "@types/lru-cache": { @@ -21,16 +33,16 @@ "integrity": "sha512-qY2IbsE6q6iWdmUyRtFqMWc2CoynQzUQz25EmHW8V9K7mq9xP6ON1ay1yDbCXlgpb0y4UJqV2EbtiMbn6nMXAg==" }, "@types/node": { - "version": "9.6.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.2.tgz", - "integrity": "sha512-UWkRY9X7RQHp5OhhRIIka58/gVVycL1zHZu0OTsT5LI86ABaMOSbUjAl+b0FeDhQcxclrkyft3kW5QWdMRs8wQ==" + "version": "9.6.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.12.tgz", + "integrity": "sha512-2Z8ziWjJbvV8hHL5YcqCG9ng+/qwUlO1gB4frBD7QI5Wm1Y1iM+AEkGVEv0S5P+aDMwTtAhPJFR4rp1uqagSig==" }, "accepts": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.2.13.tgz", "integrity": "sha1-5fHzkoxtlf2WVYw27D2dDeSm7Oo=", "requires": { - "mime-types": "2.1.18", + "mime-types": "~2.1.6", "negotiator": "0.5.3" }, "dependencies": { @@ -44,8 +56,13 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", "requires": { - "mime-db": "1.33.0" + "mime-db": "~1.33.0" } + }, + "negotiator": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.5.3.tgz", + "integrity": "sha1-Jp1cR2gQ7JLtvntsLygxY4T5p+g=" } } }, @@ -59,23 +76,16 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" }, - "asteroid": { - "version": "github:rocketchat/asteroid#a76a53254e381f9487aa2a9be3d874c9a2df6552", - "requires": { - "ddp.js": "0.5.0", - "faye-websocket": "0.11.1", - "q": "1.5.1" - } - }, "async": { "version": "0.9.2", "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=" }, "base64-url": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/base64-url/-/base64-url-1.2.1.tgz", - "integrity": "sha1-GZ/WYXAqDnt9yubgaYuwicUvbXg=" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64-url/-/base64-url-2.0.0.tgz", + "integrity": "sha1-VPckT+5/lPb04opkA0qYEjTC/h8=", + "dev": true }, "basic-auth": { "version": "1.0.4", @@ -98,17 +108,25 @@ "integrity": "sha1-wIzzMMM1jhUQFqBXRvE/ApyX+pc=", "requires": { "bytes": "2.1.0", - "content-type": "1.0.4", - "debug": "2.2.0", - "depd": "1.0.1", - "http-errors": "1.3.1", + "content-type": "~1.0.1", + "debug": "~2.2.0", + "depd": "~1.0.1", + "http-errors": "~1.3.1", "iconv-lite": "0.4.11", - "on-finished": "2.3.0", + "on-finished": "~2.3.0", "qs": "4.0.0", - "raw-body": "2.1.7", - "type-is": "1.6.16" + "raw-body": "~2.1.2", + "type-is": "~1.6.6" }, "dependencies": { + "debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "requires": { + "ms": "0.7.1" + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -124,7 +142,7 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", "requires": { - "mime-db": "1.33.0" + "mime-db": "~1.33.0" } }, "on-finished": { @@ -146,7 +164,7 @@ "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", "requires": { "media-typer": "0.3.0", - "mime-types": "2.1.18" + "mime-types": "~2.1.18" } } } @@ -161,11 +179,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" } }, "cline": { @@ -188,7 +206,7 @@ "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.13.tgz", "integrity": "sha1-DRAgq5JLL9tNYnmHXH1tq6a6p6k=", "requires": { - "mime-db": "1.33.0" + "mime-db": ">= 1.33.0 < 2" }, "dependencies": { "mime-db": { @@ -203,12 +221,22 @@ "resolved": "http://registry.npmjs.org/compression/-/compression-1.5.2.tgz", "integrity": "sha1-sDuNhub4rSloPLqN+R3cb/x3s5U=", "requires": { - "accepts": "1.2.13", + "accepts": "~1.2.12", "bytes": "2.1.0", - "compressible": "2.0.13", - "debug": "2.2.0", - "on-headers": "1.0.1", - "vary": "1.0.1" + "compressible": "~2.0.5", + "debug": "~2.2.0", + "on-headers": "~1.0.0", + "vary": "~1.0.1" + }, + "dependencies": { + "debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "requires": { + "ms": "0.7.1" + } + } } }, "connect": { @@ -217,38 +245,56 @@ "integrity": "sha1-jam8vooFTT0xjXTf7JA7XDmhtgk=", "requires": { "basic-auth-connect": "1.0.0", - "body-parser": "1.13.3", + "body-parser": "~1.13.3", "bytes": "2.1.0", - "compression": "1.5.2", - "connect-timeout": "1.6.2", - "content-type": "1.0.4", + "compression": "~1.5.2", + "connect-timeout": "~1.6.2", + "content-type": "~1.0.1", "cookie": "0.1.3", - "cookie-parser": "1.3.5", + "cookie-parser": "~1.3.5", "cookie-signature": "1.0.6", - "csurf": "1.8.3", - "debug": "2.2.0", - "depd": "1.0.1", - "errorhandler": "1.4.3", - "express-session": "1.11.3", + "csurf": "~1.8.3", + "debug": "~2.2.0", + "depd": "~1.0.1", + "errorhandler": "~1.4.2", + "express-session": "~1.11.3", "finalhandler": "0.4.0", "fresh": "0.3.0", - "http-errors": "1.3.1", - "method-override": "2.3.10", - "morgan": "1.6.1", + "http-errors": "~1.3.1", + "method-override": "~2.3.5", + "morgan": "~1.6.1", "multiparty": "3.3.2", - "on-headers": "1.0.1", - "parseurl": "1.3.2", + "on-headers": "~1.0.0", + "parseurl": "~1.3.0", "pause": "0.1.0", "qs": "4.0.0", - "response-time": "2.3.2", - "serve-favicon": "2.3.2", - "serve-index": "1.7.3", - "serve-static": "1.10.3", - "type-is": "1.6.16", + "response-time": "~2.3.1", + "serve-favicon": "~2.3.0", + "serve-index": "~1.7.2", + "serve-static": "~1.10.0", + "type-is": "~1.6.6", "utils-merge": "1.0.0", - "vhost": "3.0.2" + "vhost": "~3.0.1" }, "dependencies": { + "debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "requires": { + "ms": "0.7.1" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "fresh": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.3.0.tgz", + "integrity": "sha1-ZR+DjiJCTnVm3hYdg1jKoZn4PU8=" + }, "mime-db": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", @@ -259,7 +305,27 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", "requires": { - "mime-db": "1.33.0" + "mime-db": "~1.33.0" + } + }, + "morgan": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.6.1.tgz", + "integrity": "sha1-X9gYOYxoGcuiinzWZk8pL+HAu/I=", + "requires": { + "basic-auth": "~1.0.3", + "debug": "~2.2.0", + "depd": "~1.0.1", + "on-finished": "~2.3.0", + "on-headers": "~1.0.0" + } + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" } }, "qs": { @@ -273,7 +339,7 @@ "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", "requires": { "media-typer": "0.3.0", - "mime-types": "2.1.18" + "mime-types": "~2.1.18" } } } @@ -283,10 +349,10 @@ "resolved": "https://registry.npmjs.org/connect-multiparty/-/connect-multiparty-1.2.5.tgz", "integrity": "sha1-L6vs/cGop3S6GUhNzmYMgYqFVec=", "requires": { - "multiparty": "3.3.2", - "on-finished": "2.1.1", - "qs": "2.2.5", - "type-is": "1.5.7" + "multiparty": "~3.3.2", + "on-finished": "~2.1.0", + "qs": "~2.2.4", + "type-is": "~1.5.2" } }, "connect-timeout": { @@ -294,10 +360,20 @@ "resolved": "https://registry.npmjs.org/connect-timeout/-/connect-timeout-1.6.2.tgz", "integrity": "sha1-3ppexh4zoStu2qt7XwYumMWZuI4=", "requires": { - "debug": "2.2.0", - "http-errors": "1.3.1", + "debug": "~2.2.0", + "http-errors": "~1.3.1", "ms": "0.7.1", - "on-headers": "1.0.1" + "on-headers": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "requires": { + "ms": "0.7.1" + } + } } }, "content-disposition": { @@ -356,8 +432,8 @@ "requires": { "cookie": "0.1.3", "cookie-signature": "1.0.6", - "csrf": "3.0.6", - "http-errors": "1.3.1" + "csrf": "~3.0.0", + "http-errors": "~1.3.1" } }, "ddp.js": { @@ -366,11 +442,20 @@ "integrity": "sha1-+XfuQgeDhXEgO/eB2vtXHQrr1mo=" }, "debug": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", - "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, "requires": { - "ms": "0.7.1" + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } } }, "depd": { @@ -393,8 +478,8 @@ "resolved": "https://registry.npmjs.org/errorhandler/-/errorhandler-1.4.3.tgz", "integrity": "sha1-t7cO2PNZ6duICS8tIMD4MUIK2D8=", "requires": { - "accepts": "1.3.5", - "escape-html": "1.0.3" + "accepts": "~1.3.0", + "escape-html": "~1.0.3" }, "dependencies": { "accepts": { @@ -402,7 +487,7 @@ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", "requires": { - "mime-types": "2.1.18", + "mime-types": "~2.1.18", "negotiator": "0.6.1" } }, @@ -421,7 +506,7 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", "requires": { - "mime-db": "1.33.0" + "mime-db": "~1.33.0" } }, "negotiator": { @@ -451,27 +536,42 @@ "resolved": "https://registry.npmjs.org/express/-/express-3.21.2.tgz", "integrity": "sha1-DCkD7lxU5j1lqWFwdkcDVQZlo94=", "requires": { - "basic-auth": "1.0.4", + "basic-auth": "~1.0.3", "commander": "2.6.0", "connect": "2.30.2", "content-disposition": "0.5.0", - "content-type": "1.0.4", + "content-type": "~1.0.1", "cookie": "0.1.3", "cookie-signature": "1.0.6", - "debug": "2.2.0", - "depd": "1.0.1", + "debug": "~2.2.0", + "depd": "~1.0.1", "escape-html": "1.0.2", - "etag": "1.7.0", + "etag": "~1.7.0", "fresh": "0.3.0", "merge-descriptors": "1.0.0", - "methods": "1.1.2", + "methods": "~1.1.1", "mkdirp": "0.5.1", - "parseurl": "1.3.2", - "proxy-addr": "1.0.10", - "range-parser": "1.0.3", + "parseurl": "~1.3.0", + "proxy-addr": "~1.0.8", + "range-parser": "~1.0.2", "send": "0.13.0", "utils-merge": "1.0.0", - "vary": "1.0.1" + "vary": "~1.0.1" + }, + "dependencies": { + "debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "requires": { + "ms": "0.7.1" + } + }, + "fresh": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.3.0.tgz", + "integrity": "sha1-ZR+DjiJCTnVm3hYdg1jKoZn4PU8=" + } } }, "express-session": { @@ -482,20 +582,35 @@ "cookie": "0.1.3", "cookie-signature": "1.0.6", "crc": "3.3.0", - "debug": "2.2.0", - "depd": "1.0.1", - "on-headers": "1.0.1", - "parseurl": "1.3.2", - "uid-safe": "2.0.0", + "debug": "~2.2.0", + "depd": "~1.0.1", + "on-headers": "~1.0.0", + "parseurl": "~1.3.0", + "uid-safe": "~2.0.0", "utils-merge": "1.0.0" }, "dependencies": { + "debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "requires": { + "ms": "0.7.1" + } + }, "uid-safe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.0.0.tgz", "integrity": "sha1-p/PGymSh9qXQTsDvPkw9U2cxcTc=", "requires": { "base64-url": "1.2.1" + }, + "dependencies": { + "base64-url": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/base64-url/-/base64-url-1.2.1.tgz", + "integrity": "sha1-GZ/WYXAqDnt9yubgaYuwicUvbXg=" + } } } } @@ -505,7 +620,7 @@ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=", "requires": { - "websocket-driver": "0.7.0" + "websocket-driver": ">=0.5.1" } }, "finalhandler": { @@ -513,12 +628,20 @@ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.4.0.tgz", "integrity": "sha1-llpS2ejQXSuFdUhUH7ibU6JJfZs=", "requires": { - "debug": "2.2.0", + "debug": "~2.2.0", "escape-html": "1.0.2", - "on-finished": "2.3.0", - "unpipe": "1.0.0" + "on-finished": "~2.3.0", + "unpipe": "~1.0.0" }, "dependencies": { + "debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "requires": { + "ms": "0.7.1" + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -534,22 +657,46 @@ } } }, + "follow-redirects": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.4.1.tgz", + "integrity": "sha512-uxYePVPogtya1ktGnAAXOacnbIuRMB4dkvqeNz2qTtTQsuzSfbDolV+wMMKxAmCx0bLgAKLbBOkjItMbbkR1vg==", + "requires": { + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, "forwarded": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" }, "fresh": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.3.0.tgz", - "integrity": "sha1-ZR+DjiJCTnVm3hYdg1jKoZn4PU8=" + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true }, "has-ansi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", "requires": { - "ansi-regex": "2.1.1" + "ansi-regex": "^2.0.0" } }, "http-errors": { @@ -557,8 +704,8 @@ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.3.1.tgz", "integrity": "sha1-GX4izevUGYWF6GlO9nhhl7ke2UI=", "requires": { - "inherits": "2.0.3", - "statuses": "1.5.0" + "inherits": "~2.0.1", + "statuses": "1" } }, "http-parser-js": { @@ -571,12 +718,12 @@ "resolved": "https://registry.npmjs.org/hubot/-/hubot-3.0.1.tgz", "integrity": "sha512-hTn3pLcS77nyzYWNft5djDM45nWTyH6Ee/wLkSk5NazzMZi0R1wWQK0elAFiAK9u152SJ2x80P4IBK0Z5Gxf5w==", "requires": { - "async": "0.9.2", - "chalk": "1.1.3", - "cline": "0.8.2", + "async": ">=0.1.0 <1.0.0", + "chalk": "^1.0.0", + "cline": "^0.8.2", "coffee-script": "1.6.3", - "connect-multiparty": "1.2.5", - "express": "3.21.2", + "connect-multiparty": "^1.2.5", + "express": "^3.21.2", "log": "1.4.0", "optparse": "1.0.4", "scoped-http-client": "0.11.0" @@ -612,8 +759,8 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.2.tgz", "integrity": "sha512-wgeVXhrDwAWnIF/yZARsFnMBtdFXOg1b8RIrhilp+0iDYN4mdQcNZElDZ0e4B64BhaxeQ5zN7PMyvu7we1kPeQ==", "requires": { - "pseudomap": "1.0.2", - "yallist": "2.1.2" + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" } }, "media-typer": { @@ -632,9 +779,9 @@ "integrity": "sha1-49r41d7hDdLc59SuiNYrvud0drQ=", "requires": { "debug": "2.6.9", - "methods": "1.1.2", - "parseurl": "1.3.2", - "vary": "1.1.2" + "methods": "~1.1.2", + "parseurl": "~1.3.2", + "vary": "~1.1.2" }, "dependencies": { "debug": { @@ -663,9 +810,10 @@ "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" }, "mime": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz", - "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM=" + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", + "dev": true }, "mime-db": { "version": "1.12.0", @@ -677,7 +825,7 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.0.14.tgz", "integrity": "sha1-MQ4VnbI+B3+Lsit0jav6SVcUCqY=", "requires": { - "mime-db": "1.12.0" + "mime-db": "~1.12.0" } }, "minimist": { @@ -694,26 +842,44 @@ } }, "morgan": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.6.1.tgz", - "integrity": "sha1-X9gYOYxoGcuiinzWZk8pL+HAu/I=", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", + "integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==", + "dev": true, "requires": { - "basic-auth": "1.0.4", - "debug": "2.2.0", - "depd": "1.0.1", - "on-finished": "2.3.0", - "on-headers": "1.0.1" + "basic-auth": "~2.0.0", + "debug": "2.6.9", + "depd": "~1.1.2", + "on-finished": "~2.3.0", + "on-headers": "~1.0.1" }, "dependencies": { + "basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, "requires": { "ee-first": "1.1.1" } @@ -730,14 +896,35 @@ "resolved": "https://registry.npmjs.org/multiparty/-/multiparty-3.3.2.tgz", "integrity": "sha1-Nd5oBNwZZD5SSfPT473GyM4wHT8=", "requires": { - "readable-stream": "1.1.14", - "stream-counter": "0.2.0" + "readable-stream": "~1.1.9", + "stream-counter": "~0.2.0" } }, "negotiator": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.5.3.tgz", - "integrity": "sha1-Jp1cR2gQ7JLtvntsLygxY4T5p+g=" + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", + "dev": true + }, + "node-rest-client": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/node-rest-client/-/node-rest-client-3.1.0.tgz", + "integrity": "sha1-4L623aeyDMC2enhHzxLF/EGcN8M=", + "requires": { + "debug": "~2.2.0", + "follow-redirects": ">=1.2.0", + "xml2js": ">=0.2.4" + }, + "dependencies": { + "debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "requires": { + "ms": "0.7.1" + } + } + } }, "on-finished": { "version": "2.1.1", @@ -772,7 +959,7 @@ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.0.10.tgz", "integrity": "sha1-DUCoL4Afw1VWfS7LZe/j8HfxIcU=", "requires": { - "forwarded": "0.1.2", + "forwarded": "~0.1.0", "ipaddr.js": "1.0.5" } }, @@ -828,10 +1015,10 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", "isarray": "0.0.1", - "string_decoder": "0.10.31" + "string_decoder": "~0.10.x" } }, "response-time": { @@ -839,8 +1026,8 @@ "resolved": "https://registry.npmjs.org/response-time/-/response-time-2.3.2.tgz", "integrity": "sha1-/6cbq5UtYvfB1Jt0NDVfvGjf/Fo=", "requires": { - "depd": "1.1.2", - "on-headers": "1.0.1" + "depd": "~1.1.0", + "on-headers": "~1.0.1" }, "dependencies": { "depd": { @@ -855,6 +1042,17 @@ "resolved": "https://registry.npmjs.org/rndm/-/rndm-1.2.0.tgz", "integrity": "sha1-8z/pz7Urv9UgqhgyO8ZdsRCht2w=" }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, "scoped-http-client": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/scoped-http-client/-/scoped-http-client-0.11.0.tgz", @@ -865,20 +1063,28 @@ "resolved": "https://registry.npmjs.org/send/-/send-0.13.0.tgz", "integrity": "sha1-UY+SGusFYK7H3KspkLFM9vPM5d4=", "requires": { - "debug": "2.2.0", - "depd": "1.0.1", + "debug": "~2.2.0", + "depd": "~1.0.1", "destroy": "1.0.3", "escape-html": "1.0.2", - "etag": "1.7.0", + "etag": "~1.7.0", "fresh": "0.3.0", - "http-errors": "1.3.1", + "http-errors": "~1.3.1", "mime": "1.3.4", "ms": "0.7.1", - "on-finished": "2.3.0", - "range-parser": "1.0.3", - "statuses": "1.2.1" + "on-finished": "~2.3.0", + "range-parser": "~1.0.2", + "statuses": "~1.2.1" }, "dependencies": { + "debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "requires": { + "ms": "0.7.1" + } + }, "destroy": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.3.tgz", @@ -889,6 +1095,16 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "fresh": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.3.0.tgz", + "integrity": "sha1-ZR+DjiJCTnVm3hYdg1jKoZn4PU8=" + }, + "mime": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz", + "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM=" + }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -909,12 +1125,17 @@ "resolved": "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.3.2.tgz", "integrity": "sha1-3UGeJo3gEqtysxnTN/IQUBP5OB8=", "requires": { - "etag": "1.7.0", + "etag": "~1.7.0", "fresh": "0.3.0", "ms": "0.7.2", - "parseurl": "1.3.2" + "parseurl": "~1.3.1" }, "dependencies": { + "fresh": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.3.0.tgz", + "integrity": "sha1-ZR+DjiJCTnVm3hYdg1jKoZn4PU8=" + }, "ms": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", @@ -927,15 +1148,23 @@ "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.7.3.tgz", "integrity": "sha1-egV/xu4o3GP2RWbl+lexEahq7NI=", "requires": { - "accepts": "1.2.13", + "accepts": "~1.2.13", "batch": "0.5.3", - "debug": "2.2.0", - "escape-html": "1.0.3", - "http-errors": "1.3.1", - "mime-types": "2.1.18", - "parseurl": "1.3.2" + "debug": "~2.2.0", + "escape-html": "~1.0.3", + "http-errors": "~1.3.1", + "mime-types": "~2.1.9", + "parseurl": "~1.3.1" }, "dependencies": { + "debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "requires": { + "ms": "0.7.1" + } + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -951,7 +1180,7 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", "requires": { - "mime-db": "1.33.0" + "mime-db": "~1.33.0" } } } @@ -961,8 +1190,8 @@ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.10.3.tgz", "integrity": "sha1-zlpuzTEB/tXsCYJ9rCKpwpv7BTU=", "requires": { - "escape-html": "1.0.3", - "parseurl": "1.3.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.1", "send": "0.13.2" }, "dependencies": { @@ -994,18 +1223,38 @@ "resolved": "https://registry.npmjs.org/send/-/send-0.13.2.tgz", "integrity": "sha1-dl52B8gFVFK7pvCwUllTUJhgNt4=", "requires": { - "debug": "2.2.0", - "depd": "1.1.2", - "destroy": "1.0.4", - "escape-html": "1.0.3", - "etag": "1.7.0", + "debug": "~2.2.0", + "depd": "~1.1.0", + "destroy": "~1.0.4", + "escape-html": "~1.0.3", + "etag": "~1.7.0", "fresh": "0.3.0", - "http-errors": "1.3.1", + "http-errors": "~1.3.1", "mime": "1.3.4", "ms": "0.7.1", - "on-finished": "2.3.0", - "range-parser": "1.0.3", - "statuses": "1.2.1" + "on-finished": "~2.3.0", + "range-parser": "~1.0.3", + "statuses": "~1.2.1" + }, + "dependencies": { + "debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "requires": { + "ms": "0.7.1" + } + }, + "fresh": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.3.0.tgz", + "integrity": "sha1-ZR+DjiJCTnVm3hYdg1jKoZn4PU8=" + }, + "mime": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz", + "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM=" + } } }, "statuses": { @@ -1025,7 +1274,7 @@ "resolved": "https://registry.npmjs.org/stream-counter/-/stream-counter-0.2.0.tgz", "integrity": "sha1-3tJmVWMZyLDiIoErnPOyb6fZR94=", "requires": { - "readable-stream": "1.1.14" + "readable-stream": "~1.1.8" } }, "string_decoder": { @@ -1038,7 +1287,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { - "ansi-regex": "2.1.1" + "ansi-regex": "^2.0.0" } }, "supports-color": { @@ -1057,7 +1306,7 @@ "integrity": "sha1-uTaKWTzG730GReeLL0xky+zQXpA=", "requires": { "media-typer": "0.3.0", - "mime-types": "2.0.14" + "mime-types": "~2.0.9" } }, "uid-safe": { @@ -1065,7 +1314,7 @@ "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.4.tgz", "integrity": "sha1-Otbzg2jG1MjHXsF2I/t5qh0HHYE=", "requires": { - "random-bytes": "1.0.0" + "random-bytes": "~1.0.0" } }, "unpipe": { @@ -1093,8 +1342,8 @@ "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", "requires": { - "http-parser-js": "0.4.11", - "websocket-extensions": "0.1.3" + "http-parser-js": ">=0.4.0", + "websocket-extensions": ">=0.1.1" } }, "websocket-extensions": { @@ -1102,6 +1351,20 @@ "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==" }, + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + } + }, + "xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" + }, "yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", diff --git a/package.json b/package.json index 8be1fd611..b35a4642d 100644 --- a/package.json +++ b/package.json @@ -1,51 +1,62 @@ { - "name": "hubot-rocketchat", - "version": "2.0.0-development", - "author": { - "name": "Rocket.Chat", - "url": "https://rocket.chat/" - }, - "contributors": [ - { - "name": "Sing Li", - "email": "sing.li@rocket.chat" - }, - { - "name": "Gabriel Engel", - "email": "gabriel.engel@rocket.chat" - }, - { - "name": "Aaron Ogle", - "email": "aaron.ogle@rocket.chat" - }, - { - "name": "Tim Kinnane", - "email": "tim.kinnane@rocket.chat" - } - ], - "description": "Hubot Rocket.Chat Adapter", - "keywords": [ - "hubot", - "adapter", - "rocketchat", - "rocket", - "chat" - ], - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/RocketChat/hubot-rocketchat.git" - }, - "bugs": { - "url": "https://github.com/RocketChat/hubot-rocketchat/issues", - "email": "support@rocket.chat" - }, - "main": "index.js", - "dependencies": { - "@rocket.chat/sdk": "^0.1.0", - "hubot": "3" - }, - "peerDependencies": { - "hubot": "3" - } + "name": "hubot-rocketchat", + "version": "2.0.0-development", + "files": [ + "index.js" + ], + "author": { + "name": "Rocket.Chat", + "url": "https://rocket.chat/" + }, + "contributors": [ + { + "name": "Sing Li", + "email": "sing.li@rocket.chat" + }, + { + "name": "Gabriel Engel", + "email": "gabriel.engel@rocket.chat" + }, + { + "name": "Aaron Ogle", + "email": "aaron.ogle@rocket.chat" + }, + { + "name": "Tim Kinnane", + "email": "tim.kinnane@rocket.chat" + } + ], + "description": "Hubot Rocket.Chat Adapter", + "keywords": [ + "hubot", + "adapter", + "rocketchat", + "rocket", + "chat" + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/RocketChat/hubot-rocketchat.git" + }, + "bugs": { + "url": "https://github.com/RocketChat/hubot-rocketchat/issues", + "email": "support@rocket.chat" + }, + "main": "index.js", + "dependencies": { + "@rocket.chat/sdk": "^0.2.1", + "hubot": "3" + }, + "peerDependencies": { + "hubot": "3" + }, + "devDependencies": { + "base64-url": "^2.0.0", + "debug": "^2.6.9", + "fresh": "^0.5.2", + "mime": "^1.4.1", + "morgan": "^1.9.1", + "negotiator": "^0.6.1" + } }