README.md 13.6 KB
Newer Older
1
[asteroid]: https://www.npmjs.com/package/asteroid
2
[lru]: https://www.npmjs.com/package/lru
3

4
# Rocket.Chat Node.js SDK
5

6
Application interface for server methods and message stream subscriptions.
7 8 9

## Overview

10 11 12
Using this package third party apps can control and query a Rocket.Chat server
instance, via Asteroid login and method calls as well as DDP for subscribing
to stream events.
13

14 15 16
Designed especially for chat automation, this SDK makes it easy for bot and
integration developers to provide the best solutions and experience for their
community.
17

18 19
For example, the Hubot Rocketchat adapter uses this package to enable chat-ops
workflows and multi-channel, multi-user, public and private interactions.
20
We have more bot features and adapters on the roadmap and encourage the
21
community to implement this SDK to provide adapters for their bot framework
22 23 24 25
or platform of choice.

## API

26 27 28 29
Full API documentation can be generated locally using `yarn docs`.
This isn't in a format we can publish yet, but can be useful for development.

Below is just a summary:
30 31

---
32

33 34 35
Currently, there are two modules exported by the SDK:
- `driver` - Handles connection, method calls, room subscriptions (via Asteroid)
- `methodCache` - Manages results cache for calls to server (via LRU cache)
36

37 38
Access these modules by importing them from SDK, e.g:

39
ES6 `import { driver, methodCache } from '@rocket.chat/sdk'`
40

41
ES5 `const { driver, methodCache } = require('@rocket.chat/sdk')`
42 43 44 45 46 47 48

See [Asteroid][asteroid] docs for methods that can be called from that API.

Any Rocket.Chat server method can be called via `driver.callMethod`,
`driver.cacheCall` or `driver.asyncCall`. Server methods are not fully
documented, most require searching the Rocket.Chat codebase.

49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
### BASIC USAGE

---

The following ES6 demo uses async calls to login, join rooms, subscribe to 
message streams and respond to messages (with a callback) using provided
options to filter the types of messages to respond to.

This example can be executed with the testing instance by running `yarn start`, 
to allow manual testing, once subscription is setup try sending DMs to the bot
user and they should be logged in console.

```
import { driver } from '@rocket.chat/sdk'

async function startReceiving () {
  await driver.connect({ host: 'localhost:3000', useSsl: true })
  await driver.login({ username: 'bot', password: 'pass' })
  await driver.joinRooms(['GENERAL'])
  await driver.subscribeToMessages()
  await driver.respondToMessages((err, message, msgOpts) => {
    console.log('received message', message, msgOpts)
  }, {
    allPublic: false,
    dm: true,
    livechat: false,
    edited: true
  })
}

startReceiving()
```

82 83
#### MESSAGE OBJECTS

84 85
---

86 87 88 89 90 91 92 93 94 95
The Rocket.Chat message schema can be found here:
https://rocket.chat/docs/developer-guides/schema-definition/

The structure for messages in this package matches that schema, with a
TypeScript interface defined here: https://github.com/RocketChat/Rocket.Chat.js.SDK/blob/master/src/config/messageInterfaces.ts

The `driver.prepareMessage` method (documented below) provides a helper for
simple message creation and the `message` module can also be imported to create
new `Message` class instances directly if detailed attributes are required.

96 97 98 99 100 101 102
#### DRIVER METHODS

---

### `driver.connect(options, cb?)`

Connects to a Rocket.Chat server
103
- Options accepts `host` and `timeout` attributes
104
- Can return a promise, or use error-first callback pattern
105
- Resolves with an [Asteroid][asteroid] instance
106

107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
### `driver.disconnect()`

Unsubscribe, logout, disconnect from Rocket.Chat
- Returns promise

### `driver.login(credentials)`

Login to Rocket.Chat via Asteroid
- Accepts object with `username` and/or `email` and `password`
- Returns promise
- Resolves with logged in user ID

### `driver.logout()`

Logout current user via Asteroid
- Returns promise

### `driver.subscribe(topic, roomId)`

Subscribe to Meteor subscription
- Accepts parameters for Rocket.Chat streamer
- Returns promise
- Resolves with subscription instance (with ID)

### `driver.unsubscribe(subscription)`

Cancel a subscription
- Accepts a subscription instance
- Returns promise

### `driver.unsubscribeAll()`

Cancel all current subscriptions
- Returns promise

### `driver.subscribeToMessages()`

Shortcut to subscribe to user's message stream
- Uses `.subscribe` arguments with defaults
  - topic: `stream-room-messages`
  - roomId: `__my_messages__`
- Returns a subscription instance

150
### `driver.reactToMessages(callback)`
151

152 153 154
Once a subscription is created, using `driver.subscribeToMessages()` this method
can be used to attach a callback to changes in the message stream.

155
Fires callback with every change in subscriptions.
156 157 158 159
- Uses error-first callback pattern
- Second argument is the changed item
- Third argument is additional attributes, such as `roomType`

160 161 162 163
For example usage, see the Rocket.Chat Hubot adapter's receive function, which
is bound as a callback to this method:
https://github.com/RocketChat/hubot-rocketchat/blob/convert-es6/index.js#L97-L193

164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
### `driver.respondToMessages(callback, options)`

Proxy for `reactToMessages` with some filtering of messages based on config.
This is a more user-friendly method for bots to subscribe to a message stream.

Fires callback after filters run on subscription events.
- Uses error-first callback pattern
- Second argument is the changed item
- Third argument is additional attributes, such as `roomType`

Accepts options object, that parallels respond filter env variables:
- options.allPublic : respond to messages on all channels (or just joined)
- options.dm : respond to messages in DMs with the SDK user
- options.livechat : respond to messages in Livechat rooms
- options.edited : respond to edited messages

180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
### `driver.asyncCall(method, params)`

Wraps server method calls to always be async
- Accepts a method name and params (array or single param)
- Returns a Promise

### `driver.cacheCall(method, key)`

Call server method with `methodCache`
- Accepts a method name and single param (used as cache key)
- Returns a promise
- Resolves with server results or cached if still valid

### `driver.callMethod(method, params)`

Implements either `asyncCall` or `cacheCall` if cache exists
- Accepts a method name and params (array or single param)
- Outcome depends on if `methodCache.create` was done for the method

### `driver.useLog(logger)`

Replace the default log, e.g. with one from a bot framework
- Accepts class or object with `debug`, `info`, `warn`, `error` methods.
- Returns nothing

### `driver.getRoomId(name)`

Get ID for a room by name
- Accepts name or ID string
- Is cached
- Returns a promise
- Resolves with room ID

### `driver.getRoomName(id)`

Get name for a room by ID
- Accepts ID string
- Is cached
- Returns a promise
- Resolves with room name

### `driver.getDirectMessageRoomId(username)`

Get ID for a DM room by its recipient's name
- Accepts string username
- Returns a promise
- Resolves with room ID

### `driver.joinRoom(room)`

Join the logged in user into a room
- Accepts room name or ID string
- Returns a promise

### `driver.joinRooms(rooms)`

As above, with array of room names/IDs

### `driver.prepareMessage(content, roomId?)`

Structure message content for sending
- Accepts a message object or message text string
- Optionally addressing to room ID with second param
- Returns a message object

245
### `driver.sendMessage(message)`
246

247 248 249 250 251
Send a prepared message object (with pre-defined room ID)
- Accepts a message object
- Returns a promise that resolves to sent message object

### `driver.sendToRoomId(content, roomId)`
252

253 254 255 256 257
Prepare and send string/s to specified room ID
- Accepts message text string or array of strings
- Returns a promise or array of promises that resolve to sent message object/s

### `driver.sendToRoom(content, room)`
258 259 260 261 262 263

As above, with room name instead of ID

### `driver.sendDirectToUser(content, username)`

As above, with username for DM instead of ID
264
- Creates DM room if it doesn't exist
265 266 267 268 269

---

### METHOD CACHE

270 271 272 273
[LRU][lru] is used to cache results from the server, to reduce unnecessary calls
for data that is unlikely to change, such as room IDs. Utility methods and env
vars allow configuring, creating and resetting caches for specific methods.

274 275 276 277 278
---

### `methodCache.use(instance)`

Set the instance to call methods on, with cached results
279
- Accepts an Asteroid instance (or possibly other classes)
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
- Returns nothing

### `methodCache.create(method, options?)`

Setup a cache for a method call
- Accepts method name and cache options object, such as:
  - `max` Maximum size of cache
  - `maxAge` Maximum age of cache

### `methodCache.call(method, key)`

Get results of a prior method call or call and cache
- Accepts method name to call and key as single param
- Only methods with a single string argument can be cached (currently) due to 
the usage of this argument as the index for the cached results.

### `methodCache.has(method)`

Checking if method has been cached
- Accepts method name
- Returns bool

### `methodCache.get(method, key)`

Get results of a prior method call
- Accepts method name and key (argument method called with)
- Returns results at key

### `methodCache.reset(method, key?)`

Reset a cached method call's results
- Accepts a method name, optional key
- If key given, clears only that result set
- Returns bool

### `methodCache.resetAll()`

 Reset cached results for all methods
 - Returns nothing
319

320 321 322 323 324
---

## Getting Started

A local instance of Rocket.Chat is required for unit tests to confirm connection
325 326
and subscription methods are functional. And it helps to manually run your SDK
interactions (i.e. bots) locally while in development.
327 328 329

## Use as Dependency

330
`yarn add @rocket.chat/sdk` or `npm install --save @rocket.chat/sdk`
331 332 333 334

ES6 module, using async

```
335
import * as rocketchat from '@rocket.chat/sdk'
336

337
const asteroid = await rocketchat.driver.connect({ host: 'localhost:3000' })
338
console.log('connected', asteroid)
339 340
```

341 342
ES5 module, using callback

343
```
344
const rocketchat = require('@rocket.chat/sdk')
345

346
rocketchat.driver.connect({ host: 'localhost:3000' }, function (err, asteroid) {
347 348 349 350
  if (err) console.error(err)
  else console.log('connected', asteroid)
})
```
351 352 353 354 355

## Develop & Test

### Settings

356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
| Env var                | Description                                           |
| ---------------------- | ----------------------------------------------------- |
| `ROCKETCHAT_URL`       | URL of the Rocket.Chat to connect to                  |
| `ROCKETCHAT_AUTH`      | Set to 'ldap' to enable LDAP login                    |
| `ADMIN_USERNAME`       | Admin user password for API                           |
| `ADMIN_PASS`           | Admin user password for API                           |
| `ROCKETCHAT_USER`      | User password for SDK tests                           |
| `ROCKETCHAT_PASS`      | Pass username for SDK tests                           |
| `INTEGRATION_ID`       | ID applied to message object to integration source    |
| `ROOM_CACHE_SIZE`      | Size of cache (LRU) for room (ID or name) lookups     |
| `ROOM_CACHE_MAX_AGE`   | Max age of cache for room lookups                     |
| `DM_ROOM_CACHE_SIZE`   | Size of cache for Direct Message room lookups         |
| `DM_ROOM_CACHE_MAX_AGE`| Max age of cache for DM lookups                       |
| `LISTEN_ON_ALL_PUBLIC` | true/false, respond listens in all public channels    |
| `RESPOND_TO_LIVECHAT`  | true/false, respond listens in livechat               |
| `RESPOND_TO_DM`        | true/false, respond listens to DMs with bot           |
| `RESPOND_TO_EDITED`    | true/false, respond listens to edited messages        |
373 374 375 376 377 378 379

These are only required in test and development, assuming in production they
will be passed from the adapter implementing this package.

### Installing Rocket.Chat

Clone and run a new instance of Rocket.Chat locally, using either the internal
380
mongo or a dedicated local mongo for testing, so you won't affect any other
381 382 383
Rocket.Chat development you might do locally.

The following will provision a default admin user on build, so it can be used to
384
access the API, allowing SDK utils to prepare for and clean up tests.
385

386 387
- `git clone https://github.com/RocketChat/Rocket.Chat.git rc-sdk-test`
- `cd rc-sdk-test`
388
- `meteor npm install`
389
- `export ADMIN_PASS=pass; export ADMIN_USERNAME=sdk; export MONGO_URL='mongodb://localhost:27017/rc-sdk-test'; meteor`
390 391 392 393 394

Using `yarn` to run local tests and build scripts is recommended.

Do `npm install -g yarn` if you don't have it. Then setup the project:

395 396
- `git clone https://github.com/RocketChat/Rocket.Chat.js.SDK.git`
- `cd Rocket.Chat.js.SDK`
397 398
- `yarn`

399
### Test and Build Scripts
400

401
- `yarn test` runs tests and coverage locally (pretest does lint)
402
- `yarn test:debug` runs tests without coverage, breaking for debug attach
403 404 405
- `yarn start` run locally from source, to allow manual testing of streams
- `yarn docs` generates API docs locally, then `open docs/index.html`
- `yarn build` runs tests, coverage, compiles, and tests package for publishing
406 407 408
- `yarn test:package` uses package-preview to make sure the published node
package can be required and run only with defined dependencies, to avoid errors
that might pass locally due to existing global dependencies or symlinks.
409

410 411
`yarn:hook` is run on git push hooks to prevent publishing with failing tests,
but won't change coverage to avoid making any working copy changes after commit.
412 413 414 415 416 417 418

### Integration Tests

The node scripts in `utils` are used to prepare for and clean up after test
interactions. They use the Rocket.Chat API to create a bot user and a mock human
user (benny) for the bot to interact with. They *should* restore the pre-test
state but it is always advised to only run tests with a connection to a clean
419
local or fresh re-usable container instance of Rocket.Chat.
420 421 422 423

### Debugging

Configs are included in source for VS Code using Wallaby or Mocha Sidebar.