RocketChat / Rocket.Chat.js.SDK

Utility for apps and bots to interact with Rocket.Chat via DDP and/or API
MIT License
136 stars 94 forks source link
rocketchat-sdk

Rocket.Chat Node.js SDK

Application interface for server methods and message stream subscriptions.

Super Quick Start (30 seconds)

Create your own working BOT for Rocket.Chat, in seconds, at glitch.com.

Quick Start

Add your own Rocket.Chat BOT, running on your favorite Linux, MacOS or Windows system.

First, make sure you have the latest version of nodeJS (nodeJS 8.x or higher).

node -v
v8.9.3

In a project directory, add Rocket.Chat.js.SDK as dependency:

npm install @rocket.chat/sdk --save

Next, create easybot.js with the following:

const { driver } = require('@rocket.chat/sdk');
// customize the following with your server and BOT account information
const HOST = 'myserver.com';
const USER = 'mysuer';
const PASS = 'mypassword';
const BOTNAME = 'easybot';  // name  bot response to
const SSL = true;  // server uses https ?
const ROOMS = ['GENERAL', 'myroom1'];

var myuserid;
// this simple bot does not handle errors, different message types, server resets 
// and other production situations 

const runbot = async () => {
    const conn = await driver.connect( { host: HOST, useSsl: SSL})
    myuserid = await driver.login({username: USER, password: PASS});
    const roomsJoined = await driver.joinRooms(ROOMS);
    console.log('joined rooms');

    // set up subscriptions - rooms we are interested in listening to
    const subscribed = await driver.subscribeToMessages();
    console.log('subscribed');

    // connect the processMessages callback
    const msgloop = await driver.reactToMessages( processMessages );
    console.log('connected and waiting for messages');

    // when a message is created in one of the ROOMS, we 
    // receive it in the processMesssages callback

    // greets from the first room in ROOMS 
    const sent = await driver.sendToRoom( BOTNAME + ' is listening ...',ROOMS[0]);
    console.log('Greeting message sent');
}

// callback for incoming messages filter and processing
const processMessages = async(err, message, messageOptions) => {
  if (!err) {
    // filter our own message
    if (message.u._id === myuserid) return;
    // can filter further based on message.rid
    const roomname = await driver.getRoomName(message.rid);
    if (message.msg.toLowerCase().startsWith(BOTNAME)) {
      const response = message.u.username + 
            ', how can ' + BOTNAME + ' help you with ' +
            message.msg.substr(BOTNAME.length + 1);
      const sentmsg = await driver.sendToRoom(response, roomname);
    }
  }
}

runbot()

The above code 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.

Make sure you customize the constants to your Rocket.Chat server account.

Finally, run the bot:

node easybot.js

TBD: insert screenshot of bot working on a server

Demo

There's a simple listener script provided to demonstrate functionality locally. See the source here and/or run it with yarn start.

The start script will log to console any message events that appear in its stream. It will respond to a couple specific commands demonstrating usage of the API helpers. Try messaging the bot directly one of the following:

Overview

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.

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.

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

Docs

Full 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:


The following modules are exported by the SDK:

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

For Node 8 / ES5

const { driver, methodCache, api } = require('@rocket.chat/sdk')

For ES6 supporting platforms

import { driver, methodCache, api } from '@rocket.chat/sdk'

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.

Driver methods use an Asteroid DDP connection. See its own docs for more advanced methods that can be called from the driver.asteroid interface.

Rocket.Chat REST API calls can be made via api.get or api.post, with parameters defining the endpoint, payload and if authorization is required (respectively). See the REST API docs for details.

Some common requests for user queries are made available as simple helpers under api.users, such as api.users.onlineIds() which returns the user IDs of all online users. Run ts-node src/utils/users.ts for a demo of user query outputs.

MESSAGE OBJECTS


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.

DRIVER METHODS


driver.connect(options[, cb])

Connects to a Rocket.Chat server

driver.disconnect()

Unsubscribe, logout, disconnect from Rocket.Chat

driver.login([credentials])

Login to Rocket.Chat via Asteroid

driver.logout()

Logout current user via Asteroid

driver.subscribe(topic, roomId)

Subscribe to Meteor subscription

driver.unsubscribe(subscription)

Cancel a subscription

driver.unsubscribeAll()

Cancel all current subscriptions

driver.subscribeToMessages()

Shortcut to subscribe to user's message stream

driver.reactToMessages(callback)

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

Fires callback with every change in subscriptions.

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

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.

Accepts options object, that parallels respond filter env variables:

If rooms are given as option or set in the environment with ROCKETCHAT_ROOM but have not been joined yet this method will join to those rooms automatically.

If allPublic is true, the rooms option will be ignored.

driver.asyncCall(method, params)

Wraps server method calls to always be async

driver.cacheCall(method, key)

Call server method with methodCache

driver.callMethod(method, params)

Implements either asyncCall or cacheCall if cache exists

driver.useLog(logger)

Replace the default log, e.g. with one from a bot framework

driver.getRoomId(name)

Get ID for a room by name

driver.getRoomName(id)

Get name for a room by ID

driver.getDirectMessageRoomId(username)

Get ID for a DM room by its recipient's name

driver.joinRoom(room)

Join the logged in user into a room

driver.joinRooms(rooms)

As above, with array of room names/IDs

driver.prepareMessage(content[, roomId])

Structure message content for sending

driver.sendMessage(message)

Send a prepared message object (with pre-defined room ID)

driver.sendToRoomId(content, roomId)

Prepare and send string/s to specified room ID

driver.sendToRoom(content, room)

As above, with room name instead of ID

driver.sendDirectToUser(content, username)

As above, with username for DM instead of ID


METHOD CACHE

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.


methodCache.use(instance)

Set the instance to call methods on, with cached results

methodCache.create(method[, options])

Setup a cache for a method call

methodCache.call(method, key)

Get results of a prior method call or call and cache

methodCache.has(method)

Checking if method has been cached

methodCache.get(method, key)

Get results of a prior method call

methodCache.reset(method[, key])

Reset a cached method call's results

methodCache.resetAll()

Reset cached results for all methods


API CLIENT

We've included an API client to make it super simple for bots and apps consuming the SDK to call the Rocket.Chat REST API endpoints.

By default, it will attempt to login with the same defaults or env config as the driver, but the .login method could be used manually prior to requests to use different credentials.

If a request is made to an endpoint requiring authentication, before login is called, it will attempt to login first and keep the response token for later.

Bots and apps should manually call the API .logout method on shutdown if they have used the API.


api.loggedIn()

Returns boolean status of existing login

api.post(endpoint, data[, auth, ignore])

Make a POST request to the REST API

api.get(endpoint, data[, auth, ignore])

Make a GET request to the REST API

api.login([user])

Perform login with default or given credentials

api.logout()

Logout the current user. Returns promise

api.currentLogin

Exported property with details of the current API session

api.userFields

Exported property for user query helper default fields

api.users.all([fields])

Helper for querying all users

api.users.allNames()

Helper for querying all usernames

api.users.allIDs()

Helper for querying all user IDs

api.users.online([fields])

Helper for querying online users

api.users.onlineNames()

Helper for querying online usernames

api.users.onlineIds()

Helper for querying online user IDs


Development

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

Use as Dependency

yarn add @rocket.chat/sdk

or

npm install --save @rocket.chat/sdk

ES6 module, using async

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

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

ES5 module, using callback

const rocketchat = require('@rocket.chat/sdk')

rocketchat.driver.connect({ host: 'localhost:3000' }, function (err, asteroid) {
  if (err) console.error(err)
  else console.log('connected', asteroid)
})

Settings

Env var Description
ROCKETCHAT_URL* URL of the Rocket.Chat to connect to
ROCKETCHAT_USER* Username for bot account login
ROCKETCHAT_PASSWORD* Password for bot account login
ROCKETCHAT_AUTH Set to 'ldap' to enable LDAP login
ROCKETCHAT_USE_SSL Force bot to connect with SSL
ROCKETCHAT_ROOM Respond listens in the named channel/s (can be csv)
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
INTEGRATION_ID ID applied to message object to integration source
Advanced configs
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
Test configs
ADMIN_USERNAME Admin user password for API
ADMIN_PASS Admin user password for API

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

ROCKETCHAT_ROOM is ignored when using LISTEN_ON_ALL_PUBLIC. This option also allows the bot to listen and respond to messages from all private groups where the bot's user has been added as a member.

Installing Rocket.Chat

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

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

git clone https://github.com/RocketChat/Rocket.Chat.git rc-sdk-test
cd rc-sdk-test
meteor npm install
export ADMIN_PASS=pass; export ADMIN_USERNAME=sdk; export MONGO_URL='mongodb://localhost:27017/rc-sdk-test'; meteor

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:

git clone https://github.com/RocketChat/Rocket.Chat.js.SDK.git
cd Rocket.Chat.js.SDK
yarn

Test and Build Scripts

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.

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 for the bot to interact with. It is always advised to only run tests with a connection to a clean local or re-usable container instance of Rocket.Chat.

Debugging

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