Skip to content

Commit

Permalink
adds subscribing to presence information
Browse files Browse the repository at this point in the history
  • Loading branch information
aoberoi committed Jan 23, 2018
1 parent a6c7283 commit 9edfc55
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 7 deletions.
102 changes: 101 additions & 1 deletion docs/_pages/rtm_client.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,107 @@ If you rename a channel in which the bot is a member, you should see the handler

---

## Handling other events
### Subscribing to presence updates

Polite people try not to inundate their colleages with messages when they know they are offline.
We can teach a bot the same ettiquette by subscribing to
[presence and status information](https://api.slack.com/docs/presence-and-status) for the
users with which it interacts.

You may not need to subscribe to presence updates if your bot is okay with fetching the user's
status on-demand using the
`[WebClient#users.getPresence()]({{ site.baseurl }}{% link _reference/UsersFacet.md %}#UsersFacet+getPresence)`
method.

If you do prefer to subscribe to presence updates, each time the client connects, your bot needs to
send a list of user IDs using the `subscribePresence(userIds)` method. The `userIds` argument is an
array of user IDs and the list must be complete -- any user IDs not included are unsubscribed from
your bot.

You can get more efficient `presence_change` events by using the `batch_presence_aware` option
while connecting. If you set the option to `true` your bot will receive `presence_change` events
with a `users` property that contains an array of user IDs (instead of a `user` property with a
single user ID). See more about the event: <https://api.slack.com/events/presence_change>.

The following example shows how a bot would keep a timecard for users to record their active
time or away time.

```javascript
const { RtmClient, CLIENT_EVENTS, RTM_EVENTS, WebClient } = require('@slack/client');

// An access token (from your Slack app or custom integration - usually xoxb)
const token = process.env.SLACK_TOKEN;

// Initialize the RTM client. The dataStore option must be set to false.
const rtm = new RtmClient(token, {
dataStore: false,
useRtmConnect: true,
});

// Initialize the Web client
const web = new WebClient(token);

// Timecard data - In order for this data to survive a restart, it should be backed by a database.
// Keys: user IDs (string)
// Values: presence updates (array of { timestamp: number, presence: 'away'|'active' })
const timecards = new Map();
const getTrackedUsers = () => Array.from(timecards.keys())
const updateTimecard = (userId, presence) => {
if (!timecards.has(userId)) {
timecards.set(userId, []);
}
const userRecord = timecards.get(userId);
userRecord.push({ timestamp: Date.now(), presence });
}
const removeTimecard = (userId) => timecards.delete(userId);

// Timecard data is tracked for users in a pre-defined channel
const timeTrackingChannelId = 'C123456';


// RTM event handling
rtm.on(CLIENT_EVENTS.RTM.RTM_CONNECTION_OPEN, () => {
rtm.subscribePresence(getTrackedUsers());
});

// See: https://api.slack.com/events/presence_change
rtm.on(RTM_EVENTS.PRESENCE_CHANGE, (event) => {
event.users.forEach(userId => updateTimecard(userId, event.presence));
});

// See: https://api.slack.com/events/member_joined_channel
rtm.on('member_joined_channel', (event) => {
if (event.channel === timeTrackingChannelId) {
// When a user joins, get that user's current presence in order to update the timecard
web.users.getPresence(event.user)
.then(resp => {
updateTimecard(event.user, resp.presence);
// Update subscriptions
rtm.subscribePresence(getTrackedUsers());
})
.catch(console.error);
}
});

// See: https://api.slack.com/events/member_left_channel
rtm.on('member_left_channel', (event) => {
if (event.channel === timeTrackingChannelId) {
// When a user leaves, the timecard records are deleted
removeTimecard(event.user);
// Update subscriptions
rtm.subscribePresence(getTrackedUsers());
}
});

// Start the connecting process
rtm.start({
batch_presence_aware: true,
});
```

---

### Handling other events

Anything that happens in a Slack workspace, and that is visible to the bot (_i.e._ happens in a
channel to which the bot belongs) is communicated as an event as well. For a complete list of other
Expand Down
11 changes: 6 additions & 5 deletions docs/_reference/RTMClient.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ permalink: /reference/RTMClient
* [.dataStore](#RTMClient+dataStore) : <code>[SlackDataStore](#SlackDataStore)</code>
* [._pingTimer](#RTMClient+_pingTimer) : <code>?</code>
* [._createFacets()](#RTMClient+_createFacets)
* [.start(opts)](#RTMClient+start)
* [.start([opts])](#RTMClient+start)
* ~~[.login()](#RTMClient+login)~~
* [.nextMessageId()](#RTMClient+nextMessageId)
* [.connect(socketUrl)](#RTMClient+connect)
Expand Down Expand Up @@ -104,14 +104,15 @@ The timer repeatedly pinging the server to let it know the client is still alive
**Kind**: instance method of <code>[RTMClient](#RTMClient)</code>
<a name="RTMClient+start"></a>

### rtmClient.start(opts)
### rtmClient.start([opts])
Begin an RTM session.

**Kind**: instance method of <code>[RTMClient](#RTMClient)</code>

| Param | Type |
| --- | --- |
| opts | <code>object</code> |
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [opts] | <code>object</code> | | |
| [opts.batch_presence_aware] | <code>boolean</code> | <code>false</code> | Opt into receiving fewer `presence_change` events that can contain many users. Instead of the event containing a `user` property {string}, it would contain a `users` property {string[]}. This option is not compatible with using the `dataStore`, you must initialize the RTM client object with the `dataStore: false` option. |

<a name="RTMClient+login"></a>

Expand Down
15 changes: 15 additions & 0 deletions docs/_reference/UsersFacet.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ permalink: /reference/UsersFacet
* [.identity([options], [optCb])](#UsersFacet+identity)
* [.info(user, [optCb])](#UsersFacet+info)
* [.list([opts], [optCb])](#UsersFacet+list)
* [.lookupByEmail(email, [opts], [optCb])](#UsersFacet+lookupByEmail)
* [.setActive([optCb])](#UsersFacet+setActive)
* [.setPresence(presence, [optCb])](#UsersFacet+setPresence)

Expand Down Expand Up @@ -68,6 +69,20 @@ Lists all users in a Slack team.
| opts.include_locale | <code>?</code> | Set this to `true` to receive the locale for users. Defaults to `false` |
| [optCb] | <code>function</code> | Optional callback, if not using promises. |

<a name="UsersFacet+lookupByEmail"></a>

### usersFacet.lookupByEmail(email, [opts], [optCb])
Find a user with an email address.

**Kind**: instance method of <code>[UsersFacet](#UsersFacet)</code>
**See**: [users.lookupByEmail](https://api.slack.com/methods/users.lookupByEmail)

| Param | Type | Description |
| --- | --- | --- |
| email | <code>?</code> | An email address belonging to a user in the workspace |
| [opts] | <code>Object</code> | |
| [optCb] | <code>function</code> | Optional callback, if not using promises. |

<a name="UsersFacet+setActive"></a>

### usersFacet.setActive([optCb])
Expand Down
6 changes: 5 additions & 1 deletion lib/clients/rtm/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,11 @@ RTMClient.prototype._createFacets = function _createFacets() {

/**
* Begin an RTM session.
* @param {object} opts
* @param {object} [opts]
* @param {boolean} [opts.batch_presence_aware=false] - Opt into receiving fewer `presence_change`
* events that can contain many users. Instead of the event containing one `user` property {string},
* it would contain a `users` property {string[]}. This option is not compatible with using the
* `dataStore`, you must initialize the RTM client object with the `dataStore: false` option.
*/
RTMClient.prototype.start = function start(opts) {
// Check whether the client is currently attempting to connect to the RTM API.
Expand Down

0 comments on commit 9edfc55

Please sign in to comment.